As we can see, the structure is very similar to the previous challenge, but there is a new file CodeCheck.java. When we inspect the content of MainActivity.java we can see how the root detection is handled (same as in the previous challenge), and how the secret is checked. The function verify handles the secret checks
MainActivity.java
publicvoidverify(View view){String str;String obj =((EditText)findViewById(R.id.edit_text)).getText().toString();AlertDialog create =new AlertDialog.Builder(this).create();if(this.m.a(obj)){create.setTitle("Success!"); str ="This is the correct secret.";}else{create.setTitle("Nope..."); str ="That's not it. Try again.";}create.setMessage(str);create.setButton(-3,"OK",new DialogInterface.OnClickListener(){/* class sg.vantagepoint.uncrackable2.MainActivity.AnonymousClass3 */publicvoid onClick(DialogInterface dialogInterface,int i){dialogInterface.dismiss();}});create.show();}
where this.m.a(obj) is the function that will check whether the secret is the right one.
Who is this.m?
If we look right after the MainActivity class definition, we see
where the CodeCheck class declares a function that is implemented from the native library foo.
So, our next step is to deep dive into the native module.
CodeCheck native function
To find the logic of the bar function we will:
rename the .apk in .zip
extract the native module lib/libfoo.so
reverse it using Ghidra
Ghidra
Analyze the libfoo.so
Looking inside the binary we can identify the native function Java_sg_vantagepoint_uncrackable2_CodeCheck_bar that will check whether - the input string has 23 chars (0x17) - the string in input matches the secret using the strncmp function
The secret is directly passed to the strncmp function, so we could
Now let's get to Frida, to see how we can intercept and read the inputs passed to the strncmp function used in libfoo.so.
Frida
Because we need to trigger the strcmp function, we need first to get rid of the root detection block.
Root detection control bypass
There are different ways of bypassing the root detection controls that will shut down the app once the OK button is clicked. A "dirty" way is to overload the onClick event of the OK button, to avoid that the application will call System.exit(0).
We can achieve this using the following Frida snippet
Clicking OK will close the dialog, while the app will still run.
Root detection: bypassed
Exploit
The strncmp function has the following signature:
and is used in our Java_sg_vantagepoint_uncrackable2_CodeCheck_bar function in this way
where
*__s1 is the text passed in input from the user
(char *)&local_30 is the secret we are looking for
0x17 is the length (23 bytes)
To extract the secret we can read the inputs of the compare function and print them out when *__s1 matches our string I want your secret asap
The final script looks like
Once we call the function via Frida, and insert our magic string, the secret will be printed in the console
and when we insert the new secret in the input field we see
private CodeCheck m;
static {
System.loadLibrary("foo");
}
private native boolean bar(byte[] bArr);
/*
Dirty way of bypassing the root detection, avoiding the app to close.
*/
Java.perform(function() {
console.log("[*] Hijacking the onClick button")
var clazz_main = Java.use('sg.vantagepoint.uncrackable2.MainActivity$1')
clazz_main.onClick.implementation = function () {
console.log('onCLick() is replaced ');
};
});
int strncmp(char *__s1,char *__s2,size_t __n)
iVar1 = strncmp(__s1,(char *)&local_30,0x17);
function extractSecret(){
/*
To use this function, we need to pass in input an argument with 23 chars. We chose: I want your secret asap
*/
console.log()
console.log('[*] ACTION NEEDED: Insert the string "I want your secret asap" as input')
console.log()
setTimeout(function(){
Interceptor.attach(Module.findExportByName('libfoo.so', 'strncmp'),{
onEnter: function(args){
if( Memory.readUtf8String(args[1]).length == 23 && Memory.readUtf8String(args[0]).includes("I want your secret asap")){
console.log()
console.log()
console.log("*******SECRET********")
console.log(Memory.readUtf8String(args[1]))
console.log("*******SECRET********")
console.log()
console.log()
}
},
onLeave: function(retval){
}
});
},2000);
}