Mobile Authentication Bypass
What Is Broken and Why
Mobile authentication is broken when the authentication check is event-driven (callback-only) rather than cryptographically bound to a Keystore/Secure Enclave key. An app that calls BiometricPrompt / LAContext.evaluatePolicy() and then checks a boolean success return can be bypassed by hooking the callback with Frida and forcing true. Truly secure biometric auth requires a CryptoObject (Android) or SecAccessControl with biometry binding (iOS) — without this, the biometric check has no cryptographic consequence and can be bypassed at the application layer.
Key Signals
BiometricPromptused without aCryptoObjectparameter (event-only, not key-bound)LAContext.evaluatePolicy(_:localizedReason:reply:)with no Keychain operation tied to authuserAuthenticationValidityDurationSeconds > 0with large values (minutes/hours)- No
kSecAccessControlBiometryCurrentSetflag — new enrollments silently unlock Keychain items setUserAuthenticationRequired(false)on a Keystore key intended for biometric-gated operations- Fallback path (device PIN) bypasses Keystore binding constraints
onAuthenticationSucceededcallback contains business logic without usingcryptoObject.cipher
Methodology
Android:
- Decompile APK — search for
BiometricPrompt,FingerprintManager,KeyguardManager - Check
BiometricPrompt.authenticate()call: does it pass aCryptoObject? - If no
CryptoObject→ callback-only auth → hookable with Frida - Check Keystore key spec:
setUserAuthenticationRequired(true)andsetInvalidatedByBiometricEnrollment(true) - Check
userAuthenticationValidityDurationSeconds: 0 = require auth on every use (correct); >0 = time-window (weaker) - Test fallback: trigger failed biometric → does PIN bypass the Keystore key requirement?
- Frida hook: override
onAuthenticationSucceededto fire without user touching sensor
iOS:
- Search IPA source for
LAContext,evaluatePolicy,kLAPolicyDeviceOwnerAuthenticationWithBiometrics - Check Keychain item:
SecAccessControlCreateWithFlagswith.biometryCurrentSetor.userPresence? - If auth is event-only (just checks LAContext result, no Keychain op) → bypass with Frida
- Verify
evaluatedPolicyDomainStateis checked on launch to detect enrollment changes - Frida: hook
LAContext.evaluatePolicyreply block, forceerror=nilto simulate success
Payloads & Tools
// Frida — Android: bypass BiometricPrompt (event-only auth)
Java.perform(function() {
var BiometricPrompt = Java.use("androidx.biometric.BiometricPrompt$AuthenticationCallback");
BiometricPrompt.onAuthenticationFailed.implementation = function() {
this.onAuthenticationSucceeded(Java.use(
"androidx.biometric.BiometricPrompt$AuthenticationResult").$new(null, 1));
};
});
// Frida — iOS: bypass LAContext evaluatePolicy
var LAContext = ObjC.classes.LAContext;
var evaluatePolicy = LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(evaluatePolicy.implementation, {
onEnter: function(args) {
var replyBlock = new ObjC.Block(args[4]);
replyBlock.implementation = function(success, error) {
replyBlock.implementation(1, null); // force success
};
}
});
# objection — Android: bypass biometric
android hooking watch class_method \
androidx.biometric.BiometricPrompt\$AuthenticationCallback.onAuthenticationSucceeded
# objection — iOS: bypass LAContext
ios jailbreak disable # in some versions triggers auth bypass
Bypass Techniques
- Frida callback hook — force
onAuthenticationSucceeded/ LAContext reply to return success without biometric - New enrollment attack — add attacker's fingerprint to device if screen is accessible; app without
kSecAccessControlBiometryCurrentSetdoesn't detect enrollment change - Fallback escalation — trigger lockout of biometrics to force PIN fallback, then bypass PIN check in app logic
- Time-window key abuse — if
userAuthenticationValidityDurationSecondsis set to hours, re-use the authenticated key window after the user walked away - Root/jailbreak + memory patch — on rooted devices, patch the auth result check directly in memory
Exploitation Scenarios
Scenario 1 — Frida Biometric Bypass (Android)
Setup: Banking app uses BiometricPrompt without CryptoObject; success triggers fund transfer unlock in callback. → Trigger: Frida hooks onAuthenticationSucceeded, fires it without biometric. → Impact: Full access to transfer functionality without the user's fingerprint.
Scenario 2 — New Enrollment Unlock (iOS)
Setup: Vault app stores secret in Keychain with .userPresence access control (no .biometryCurrentSet). → Trigger: Attacker adds their fingerprint to victim's unlocked device settings. → Impact: Attacker's Touch ID unlocks the Keychain secret.
Scenario 3 — Validity Duration Abuse (Android)
Setup: App sets userAuthenticationValidityDurationSeconds(300) (5 minutes). → Trigger: User authenticates once; attacker immediately uses the device. → Impact: Keystore key operations succeed for 5 minutes without additional auth.
False Positives
BiometricPromptwithCryptoObject— properly bound, not bypassable via callback hook- Keychain items with
kSecAccessControlBiometryCurrentSet— enrollment change invalidates access - Auth bypass succeeds in debug/test build but not in production (release build strips debug flags)
evaluatePolicyresult is only used for UX (greyed-out UI), not for actual secret release
Fix Patterns
// Android — key-bound biometric (correct)
val keyStore = KeyStore.getInstance("AndroidKeyStore")
val keyGen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGen.init(KeyGenParameterSpec.Builder("bioKey", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setUserAuthenticationRequired(true)
.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG) // 0 = per-use
.setInvalidatedByBiometricEnrollment(true)
.build())
// Pass CryptoObject to BiometricPrompt.authenticate()
// iOS — Keychain bound to biometry set (correct)
let access = SecAccessControlCreateWithFlags(nil,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
[.biometryCurrentSet], nil)!
// Any new enrollment invalidates this Keychain item
Related Skills
[[auth-bypass]] on the web covers the same conceptual space — client-supplied flags and callback-only checks — but in a mobile runtime context. [[mobile-weak-crypto]] enables auth bypass when biometric auth is event-only rather than key-bound: without a CryptoObject, there is no cryptographic consequence to the biometric check. [[mobile-resilience]] controls like root/jailbreak detection are a prerequisite defense — Frida-based auth bypass requires an attached debugger or instrumentation framework that resilience controls are designed to detect.