Objective: A secret string is hidden somewhere in this app. Find a way to extract it.
There are several ways to complete this challenge but for the sake of simplicity I’m going to show how to do it by only patching the smali code of the application.
Let’s install the application via adb and run it.
adb install UnCrackable-Level1.apk
It seems like there’s some kind of root detection in place.
Bypassing root detection Let’s take a look at the source code in jadx-gui.
The code is obfuscated but we can see that during the initialization of the MainActivity function c.a(), c.b() and c.c() are called. Let’s take a closer look.
Functions of Class C are responsible for checking if the application is running in a rooted environment or not.
Function c.a() checks for the existence of “su“ binaries on the device and returns a boolean.
"Check for 'su' binary: Su binary check is to identify the superuser in the device. This binary is installed when you try to root your phone using apps like kinguser or via fastboot in Android. These files are necessary so that one can root their phone and become the superuser. The existence of this binary can be checked from the following paths." source
Function c.b() checks for the existence of “test-keys” on the device and returns a boolean.
"Check for Test-Keys: Test-Keys has to do with how the kernel is signed when it is compiled. By default, stock Android ROMs from Google are built with release-keys tags. Test-Keys means it is signed with a custom key generated by a third-party developer. Specifically, it will check in build properties('android.os.Build.TAGS') for test-keys." source
Function c.c() checks for various file paths which indicate if a device is rooted or not and returns a boolean.
/system/app/Superuser.apk, /system/xbin/daemonsu, /system/etc/init.d/99SuperSUDaemon, /system/bin/.ext/.su, /system/etc/.has_su_daemon, /system/etc/.installed_su_daemon, /dev/com.koushikdutta.superuser.daemon/
If any of these 3 functions return true a popup message would appear saying “This is unacceptable. The App is now going to exit.”. When we click OK the function a() would terminate the activity by calling System.exit(0).
The simplest solution is:
- Unpack the apk
- Remove the line responsible for the termination
- Repackage the apk with apktool.
Unpacking the .apk file
apktool d UnCrackable-Level1.apk
Remove the corresponding line of smali code
- Open /UnCrackable-Level1/smali/sg/vantagepoint/uncrackable1/'MainActivity$1.smali' in any text editor
- Remove line 41 and save it
Repackage the apk
apktool b UnCrackable-Level1 -o UnCrackable-Level1.modified.apk
Sign it before installing, in this case I’m going to use objection’s keystore.
objection patchapk --source UnCrackable-Level1.modified.apk
adb uninstall owasp.mstg.uncrackable1
adb install UnCrackable-Level1.modified.objection.apk
Root detection is successfully bypassed.
Finding the secret
In a nutshell: In the main activity there’s a textbox which takes some input and after clicking “Verify” it compares the entered value to the secret value. If we didn’t supply the correct value we’ll get an error message.
Take a look at the source code
I wonder what function a.a() is about?
The hex encoded value (8d127684cbc37c17616d806cf50473cc) is converted to bytes (b()) and passed to sg.vantagepoint.a.a.a along with a base64 encoded value (5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=). The str argument (the user’s input) is compared with the string in bArr variable using str.equals. It returns true if there’s a match and false if there isn’t.
Take a look at a.a.a()
This method has 2 arguments, bArr and bArr2. bArr is used as the key in new SecretKeySpec with AES/ECB/PKCS7Padding cipher mode. After the cipher gets created with the encryption key a byte array is returned using the cipher to decrypt bArr2 (return instance.doFInal(bArr2)).
Let’s hook onto method a.a.a() with objection and capture the return value.
objection -g owasp.mstg.uncrackable1 explore
android hooking watch class_method sg.vantagepoint.a.a.a --dump-args --dump-return
Enter some test string and monitor the results
a.a.a()’s return value is: 73,32,119,97,110,116,32,116,111,32,98,101,108,105,101,118,101
Let’s convert this byte array into a string with Java
javac ByteArraytoString.java && java ByteArraytoString
Tools used