This is the solution of the OWASP Uncrackable Android Level3. The binary can be found under https://github.com/OWASP/owasp-mstg/tree/master/Crackmes.
In my previous post I detailed:
- how to patch an apk file with apktool
- how to patch a binary with IdaPro
- how to debug an android binary with gdb
I will not detail these steps here. This challenge is very similar to the previous one. I will only detail the differences.
I started the analysis with loading the apk file into Jadx-GUI. I opened the MainActivity first.
This application also uses a native library called foo. The passed password is verified in the library. The important difference is that the application starts with an integrity check. This check is in the method verifyLibs:
The application calculates CRC for each architecture native library and compares it with the stored CRC values. The stored CRCs are stored as string resources. Then the application calculates the CRC for the dex file and compares it with a function return value. The function resides in the native library and it is called baz. The CRC values are logged with the expected values, too. In case the calculated CRC differs from the expected one, the tampered variable is set to 31337.
Let us go back to the onCreate method. First there is an integrity check. Then the native library is initialized with a value (pizza), and there is a debugger detection. Finally there is a root detection and tamper detection. If the application is rooted or tampered, the showDialog method is called, which shows an alert dialog and exits the application. The simplest solution to tackle with these protections is patching the application and removing the showDialog method.
The next step is analyzing the native library. Here are the imported functions:
The ptrace is used for anti-debug purpose. There is no strncmp! The interesting thing here is the fopen. I opened the place where fopen is used. This can be accomplished in IdaPro with pressing Ctrl+X.
The native library opens the /proc/self/maps and searches for frida or exposed. If it finds any of these, the goodbye function is called which raises an exception. This check runs in the sub_73D0, which is executed when the library is loaded into the memory. The .init_array segment contains code, that is executed when the library is loaded into the memory. This is an ideal place for anti-debugging and anti tampering check. In case any problem, the library is not loaded.
To handle it, I opened the sub_73D0 function and replaced the first instruction with RET. After patching the native library and the apk file, I was able to run the program. I also managed to attach gdb to the process.
The check of the password happens in the bar function.
I tried to debug the application, but the function excited prematurely. I found out that there was a certain value, that was 1 instead of 2. This value was incremented in the init function and the sub_73D0 function. Since I patched the application, this last one was not executed and the value was not incremented twice, but only once. Checking this value ensures, that the native library is initialized properly. If the native library is not initialized properly, the password check will fail.
I modified the value of the register X9 in the debugger. Now I reached the second check: the length check. The length of the entered password should be 0x18 = 24. I entered 24 character and finally reached the sub_7224, where the password comparation occurs.
I dumped the values of X1, X2 and X3 registers.
Then dumped the memory at these locations:
X0 contains the string I entered. X2 contains the value with which the native library was initialized. X1 contains a binary array.
I reverse engineered the sub_7224 function.
The two array (X1 and X2) are XOR-ed and compared with X1 array, byte by byte (EOR = Exclusive OR). I created a python script, that does the same.
#!/usr/bin/env python secret = '1d0811130f1749150d0003195a1d1315080e5a0017081314'.decode('hex') key = 'pizzapizzapizzapizzapizz1' password = "" for i in range(24): password += chr((ord(secret[i]) ^ ord(key[i]))) print password