Dynamic Hooking and Overwriting of Native Android Password Validation Using Frida
Dynamic Hooking: Bypassing Android Password Validation and Manipulating UI with Frida
Introduction
In this exploration, we leverage Frida, a powerful dynamic instrumentation toolkit, to manipulate native Android password validation logic and manipulate UI elements programatically. These techniques showcase how we can reveal vulnerabilities and assess applications.
Password Validation Hooking
We begin by dynamically intercepting the password validation function in the native library. The library libnative-lib.so
serves as the artifact providing the functionality, and the script attaches to the function Java_com_optiv_ndkcrackme_MainActivity_b
, which is responsible for handling password validation:
Java.perform(function () {
var lib = Module.findExportByName("libnative-lib.so", "Java_com_optiv_ndkcrackme_MainActivity_b");
if (lib) {
Interceptor.attach(lib, {
onEnter: function (args) {
console.log("Password input: " + Memory.readUtf8String(args[1]));
},
onLeave: function (retval) {
console.log("Original return value: " + retval.toInt32());
retval.replace(1); // Force valid password
console.log("Modified return value: " + retval.toInt32());
}
});
console.log("Password bypass hook installed.");
} else {
console.log("Target function not found.");
}
});
Note: The app must be in the foreground to ensure that the native library is loaded into memory and accessible for hooking.
Figure 1: Demonstrating password bypass with Frida
Method Hooking for Password Validation
After enumerating the methods available in MainActivity
, we identified b(String)
as the method responsible for password validation. Using Frida, we can hook this method and implement custom logic for password verification. This allows us to specify certain passwords that should be accepted or rejected dynamically during runtime.
Implementation
The following script overrides the behavior of b(String)
in MainActivity
. It accepts the password "correctPassword" and rejects "securePassword123", while defaulting to the original validation logic for other inputs:
Java.perform(function () {
var MainActivity = Java.use("com.optiv.ndkcrackme.MainActivity");
MainActivity.b.implementation = function (password) {
console.log("Password validation called with input: " + password);
if (password === "correctPassword") {
console.log("Accepting password: " + password);
return true; // Accept "correctPassword"
} else if (password === "securePassword123") {
console.log("Rejecting password: " + password);
return false; // Reject "securePassword123"
}
// Call original method for other inputs
var result = this.b(password);
console.log("Original return value: " + result);
return result;
};
});
Running this script produces the following results. We see a specific password is allowed and one is explicitly denied access:
Password validation called with input: securePassword123
Rejecting password: securePassword123
Password validation called with input: correctPassword
Accepting password: correctPassword
This demonstrates how dynamic method hooking can be used to manipulate application behavior, enabling targeted testing and analysis of password validation logic.
Text Replacement Hook
The following section discusses the dynamic text replacement hook implemented using Frida. This script intercepts calls to the setText
method of android.widget.TextView
, replacing specific text values at runtime. The technique demonstrates how UI manipulation can enhance application testing and highlight potential vulnerabilities.
Implementation
The script attaches to all overloads of the setText
method, replacing occurrences of "Password rejected!" with "Access granted!". This ensures a seamless user experience modification, allowing us to bypass textual restrictions dynamically.
Java.perform(function () {
console.log("Starting text replacer...");
var TextView = Java.use("android.widget.TextView");
TextView.setText.overloads.forEach(function (overload) {
overload.implementation = function (arg1) {
if (arg1 && arg1.toString) {
var originalText = arg1.toString();
console.log("Original Text: " + originalText);
// Prevent modifying already modified text
if (!originalText.includes("Access granted")) {
var replacedText = originalText.replace("Password rejected!", "Access granted!");
console.log("Replaced Text: " + replacedText);
// Ensure correct type
var javaString = Java.use("java.lang.String").$new(replacedText);
return overload.call(this, javaString);
}
}
return overload.call(this, arg1);
};
});
console.log("Text replacer installed.");
});
Execution and Output
Running the script outputs detailed logs of original and replaced text values:
Starting text replacer...
Text replacer installed.
Original Text: Password rejected!
Replaced Text: Access granted!
Original Text: Access granted!
Original Text: Value NOT found.
Replaced Text: Value NOT found.
Original Text: Value stored!
Replaced Text: Value stored!
This demonstrates how text replacement can be applied selectively based on predefined rules, ensuring critical user feedback remains unaltered while bypassing unnecessary restrictions.
Conclusion
Dynamic analysis using Frida provides unparalleled insights into Android application security. These techniques uncover potential vulnerabilities and highlight the importance of secure coding practices in native libraries and sensitive storage operations.
Ethical Disclaimer
These methodologies are intended solely for educational and ethical use. Ensure compliance with all legal regulations and obtain proper authorization before applying these techniques.
Comments
Post a Comment