Introduction
In the landscape of Android application security, understanding how cryptographic keys are derived is paramount. Key Derivation Functions (KDFs) are critical components that transform human-memorable passwords or other initial secret material into cryptographic keys. Weak or poorly implemented KDFs can compromise an entire application’s security, even if the subsequent encryption is robust. This expert-level guide will delve into identifying common KDFs within Android applications through static and dynamic analysis and, more importantly, demonstrate how to effectively hook and inspect their parameters using Frida.
By intercepting KDF inputs (passwords, salts, iteration counts) and outputs (derived keys), security researchers and penetration testers can gain deep insights into an app’s cryptographic practices, identify vulnerabilities, and even replicate key derivation outside the application context.
Understanding Key Derivation Functions (KDFs)
KDFs are algorithms that stretch a relatively low-entropy secret (like a password) into a high-entropy key suitable for cryptographic operations. They are designed to be computationally intensive and resistant to brute-force attacks, especially when properly configured with sufficient iteration counts and strong salts.
Common KDFs in Android Development:
- PBKDF2 (Password-Based Key Derivation Function 2): Widely used and specified in PKCS #5. It takes a password, a salt, an iteration count, and a desired key length to produce a derived key. Implemented in Java’s
SecretKeyFactory. - scrypt: A more memory-hard KDF designed to make large-scale custom hardware attacks prohibitively expensive.
- Argon2: The winner of the Password Hashing Competition (PHC), offering adjustable memory, time, and parallelism costs.
Our primary target for hooking in Android Java applications will often be PBKDF2 due to its prevalence and common implementation through standard Java Cryptography Architecture (JCA) APIs.
Prerequisites
To follow this guide, you’ll need:
- A rooted Android device or an emulator with root access.
- ADB (Android Debug Bridge) installed and configured on your host machine.
- Frida server installed on the Android device and Frida client on your host.
- A decompiler/disassembler like Jadx or Ghidra for static analysis.
- Basic understanding of Java/Kotlin and Android app structure.
Identifying KDFs in Android Applications
1. Static Analysis with Jadx/Ghidra
Start by decompiling the APK using Jadx or loading it into Ghidra. Our goal is to locate calls to KDF-related classes and methods.
Keywords and Patterns to Search For:
SecretKeyFactory: The primary Java class for KDFs like PBKDF2.PBKDF2WithHmacSHA1,PBKDF2WithHmacSHA256: Common algorithm strings passed toSecretKeyFactory.getInstance().PBEKeySpec: Used with PBKDF2, this class holds the password, salt, and iteration count.SecretKeySpec: Often used to wrap derived keys for use in ciphers.KeySpec,AlgorithmParameterSpec: Interfaces related to key specifications.- Hex encoding/decoding: Look for conversions of byte arrays to/from hexadecimal strings, often associated with salts or derived keys.
Example Search Flow in Jadx:
- Open the APK in Jadx.
- Use the search bar (Ctrl+Shift+F) to look for
PBKDF2orSecretKeyFactory. - Once a match is found, navigate to the calling code to understand how
getInstance()andgenerateSecret()are invoked. - Pay close attention to where the
PBEKeySpecobject is instantiated, as this is where the raw password, salt, and iteration count are provided.
2. Dynamic Analysis with Frida (Confirmation)
While static analysis helps pinpoint potential KDFs, dynamic analysis with Frida can confirm their usage in real-time, reveal runtime parameters, and help bypass obfuscation.
Setting Up the Frida Environment
1. Download Frida Server: Obtain the correct Frida server binary for your Android device’s architecture (e.g., `frida-server-16.1.4-android-arm64`).
wget https://github.com/frida/frida/releases/download/16.1.4/frida-server-16.1.4-android-arm64.xz
unxz frida-server-16.1.4-android-arm64.xz
2. Push to Device:
adb push frida-server-16.1.4-android-arm64 /data/local/tmp/frida-server
3. Set Permissions & Run:
adb shell
su
chmod 755 /data/local/tmp/frida-server
/data/local/tmp/frida-server &
4. Install Frida Client:
pip install frida-tools
Verify installation by running `frida-ps -U`.
Crafting Frida Hooks for KDFs
Our goal is to intercept the generateSecret() method of SecretKeyFactory, specifically when it’s called with a PBEKeySpec. This will allow us to capture the password, salt, and iteration count.
Example: Hooking PBKDF2 via SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
Let’s assume static analysis revealed that your target app uses SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") and then calls generateSecret(PBEKeySpec keySpec).
Here’s a Frida script (`frida_kdf_hook.js`) to intercept these calls:
Java.perform(function() {
console.log("[*] Frida script loaded: Hooking Key Derivation Functions");
var SecretKeyFactory = Java.use("javax.crypto.SecretKeyFactory");
var PBEKeySpec = Java.use("javax.crypto.spec.PBEKeySpec");
var SecretKey = Java.use("javax.crypto.SecretKey");
// Hooking getInstance to see what algorithms are requested
SecretKeyFactory.getInstance.overload('java.lang.String').implementation = function(algorithm) {
console.log("[*] SecretKeyFactory.getInstance(" + algorithm + ") called");
if (algorithm.toLowerCase().includes("pbkdf2")) {
console.log(" [+] Detected PBKDF2 KDF instantiation!");
}
return this.getInstance(algorithm);
};
// Hooking generateSecret(KeySpec)
SecretKeyFactory.prototype.generateSecret.overload('java.security.spec.KeySpec').implementation = function(keySpec) {
console.log("[*] SecretKeyFactory.generateSecret(KeySpec) called");
// Check if the KeySpec is an instance of PBEKeySpec
if (PBEKeySpec.isInstance(keySpec)) {
console.log(" [+] KeySpec is PBEKeySpec. Extracting details...");
var pbeKeySpec = Java.cast(keySpec, PBEKeySpec);
var password = Java.cast(pbeKeySpec.getPassword().map(function(c) { return String.fromCharCode(c); }).join(''), Java.use('java.lang.String'));
var salt = pbeKeySpec.getSalt();
var iterationCount = pbeKeySpec.getIterationCount();
var keyLength = pbeKeySpec.getKeyLength();
console.log(" Password: " + password);
console.log(" Salt (Hex): " + Array.from(salt).map(function(b) { return ('0' + (b & 0xFF).toString(16)).slice(-2); }).join(''));
console.log(" Iteration Count: " + iterationCount);
console.log(" Key Length (bits): " + keyLength);
var derivedKey = this.generateSecret(keySpec);
var derivedKeyBytes = derivedKey.getEncoded();
console.log(" Derived Key (Hex): " + Array.from(derivedKeyBytes).map(function(b) { return ('0' + (b & 0xFF).toString(16)).slice(-2); }).join(''));
return derivedKey;
}
// If not PBEKeySpec, just call the original method
return this.generateSecret(keySpec);
};
// Optional: Hook SecretKeySpec constructor to catch keys being used directly
var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function(keyBytes, algorithm) {
console.log("[*] SecretKeySpec constructor called with algorithm: " + algorithm);
console.log(" [+] Key Bytes (Hex): " + Array.from(keyBytes).map(function(b) { return ('0' + (b & 0xFF).toString(16)).slice(-2); }).join(''));
this.$init(keyBytes, algorithm);
};
});
Running the Frida Script:
frida -U -f com.your.package.name -l frida_kdf_hook.js --no-pause
Replace `com.your.package.name` with the actual package name of the target application. The `–no-pause` flag ensures the app starts immediately after Frida injects the script.
Analyzing the Output and Practical Applications
Once the script is running and the application performs a KDF operation, you will see output in your terminal similar to this:
[*] Frida script loaded: Hooking Key Derivation Functions
[*] SecretKeyFactory.getInstance(PBKDF2WithHmacSHA1) called
[+] Detected PBKDF2 KDF instantiation!
[*] SecretKeyFactory.generateSecret(KeySpec) called
[+] KeySpec is PBEKeySpec. Extracting details...
Password: mySecretPassword123
Salt (Hex): A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6
Iteration Count: 10000
Key Length (bits): 256
Derived Key (Hex): 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF
Interpreting the Logs:
- Password: The raw password used by the application. This is a critical piece of information for security audits.
- Salt (Hex): The cryptographic salt, often a random string, used to prevent rainbow table attacks.
- Iteration Count: The number of times the KDF algorithm is run. Higher counts mean more computational work and better resistance to brute-force attacks.
- Key Length (bits): The desired length of the derived key.
- Derived Key (Hex): The final key bytes used for symmetric encryption or other cryptographic operations.
Practical Applications:
- Password Cracking/Auditing: If you recover the password, salt, and iteration count, you can attempt to crack weak passwords offline using tools like Hashcat or John the Ripper.
- Replicating Cryptography: Knowing the derived key allows you to replicate the application’s cryptographic operations (encryption/decryption) outside the app, aiding in data analysis or vulnerability exploitation.
- Identifying Weak KDF Parameters: Low iteration counts or predictable salts indicate a weak implementation that should be flagged in a security report.
- Bypassing Authentication: In some cases, if the authentication token or session key is derived via a predictable or weak KDF, controlling the inputs could lead to authentication bypasses.
Conclusion
Frida provides an invaluable toolkit for dynamic analysis of Android applications, particularly when diving into cryptographic implementations. By mastering the art of identifying and hooking KDFs, security researchers can uncover sensitive information, evaluate the strength of an application’s key derivation practices, and ultimately contribute to building more secure mobile software. This deep dive into KDF hooking serves as a fundamental step towards comprehensive Android application penetration testing and reverse engineering.
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 →