OWASP UnCrackable App for Android Level 1 — Walkthrough

Tobias König
5 min readMar 5, 2022

--

The UnCrackable App for Android Level 1 is a reverse-engineering challenge. The app is used as an example in the Mobile Security Testing Guide.

In this blog post, we’ll use static analysis to find the hidden secret. We’ll also showcase how to bypass several root and debug detection checks by patching the app’s Smali code. In addition, we’ll use frida for dynamic instrumentation to modify the app behaviour during runtime.

Exploring the App

Let’s start by exploring the app in a rooted emulator (API 29) in Android Studio. Once we run the app, we see the following error message:

Root detection error message

The app exits immediately after we tap the OK button. Time for some static analysis to better understand the app behaviour.

Analyzing the App in jadx-gui

We’ll use jadx-gui, a Dex to Java decompiler, for static analysis. The main activity of this app contains three methods to detect root as well as a method to detect if the app is debuggable:

All of these methods are called as soon as the app starts. We can only access the additional functionality of this app if we bypass these checks. We should then be able to access the verify() method that is triggered when we tap the VERIFY button in the app:

Source code of method to verify the user input text.

The code snippet shows that the verify() method calls another method called a.a(). The parameter obj is the string we enter in the text field. This means we only see the success message if the a.a() method evaluates to true.

Let’s look at the implementation of this method:

Source code of method that contains a hex value and a Base64-encoded value.

We can see that a.a() is calling yet another method with 2 parameters that look like a hex value and a Base64-encoded value. As the log message in the catch block suggests, these parameters are likely used for an AES crypto operation.

Analyzing the callee reveals the following code:

The a() method uses the javax.crypto.Cipher class to decrypt (opmode 2 within instance.init()) the secret in the bArr2 byte array.

As the key and the secret are hard-coded, we can simply decrypt the secret. Let’s use CyberChef for the decryption process:

The secret string we need to enter is I want to believe .

Bypassing the Protection Measures

To confirm that our secret is correct, we need to enter it in the text field within the app. Let’s explore three ways to enter the code.

Using a Non-Rooted Device

The easiest way to enter the secret is to switch to a non-rooted emulator in Android Studio. We can simply pick an emulator with Play Store enabled as these devices are never rooted by default.

Entering the string we uncovered earlier confirms we actually found the right secret:

Patching the App by Modifying its Smali Code

Let’s assume we want to keep using our rooted device. In this scenario, we could bypass the protection mechanisms by decompiling the app, modifying its Smali code, and then recompiling the app.

We’ll use apktool for the decompilation process:

apktool d UnCrackable-Level1.apk

apktool retrieves the Dex files, which contain the Dalvik bytecode for this app. However, bytecode is not human-readable. Fortunately, apktool internally uses the baksmali disassembler to transform the Dalvik bytecode into a much more readable representation called Smali.

Let’s analyse a snippet of the Smali file for the main activity:

This snippet contains the implementation of the onCreate() method we reviewed earlier in jadx-gui. As we want to bypass any root detection and debug checks, we simply have to remove any lines which are associated with this functionality.

Our modified code only calls the super class and then sets the content view so the GUI is displayed correctly:

Let’s now recompile and sign the app with a keystore we create:

# Recompile the app.
apktool b UnCrackable-Level1 -o patched.apk
# Create a new keystore. Our keystore password will be 123456. keytool -genkey -v -keystore custom.keystore -alias mykeyaliasname -keyalg RSA -keysize 2048 -validity 10000# Sign the APK.
jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore custom.keystore -storepass 123456 patched.apk mykeyaliasname
# Verify the signature.
jarsigner -verify patched.apk
# Zipalign the APK.
zipalign 4 patched.apk patched-final.apk

All that’s left to do now is to install the new APK on a rooted device and then run it.

App UI without any error messages being displayed.

We successfully bypassed all checks and can now enter the secret.

Using frida

We can also use dynamic instrumentation to bypass all checks. Let’s write a simple frida script that sets the return values of all checks to false. Notice that we don’t even have to worry about the implementation of these methods because we overwrite them anyways. We also add a log statement so we know each method is executed as expected:

Let’s now push the x86 version of frida-server to our emulator and run it:

adb root
adb push frida-server-15.1.14-android-x86 /data/media/
adb shell
cd /data/media
chmod +x frida-server-15.1.14-android-x86
./frida-server-15.1.14-android-x86 &

Next, we run the frida client and load our JS script once the app starts:

frida -U -f owasp.mstg.uncrackable1 -l root-bypass.js                               
____
/ _ | Frida 15.1.14 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
Spawned `owasp.mstg.uncrackable1`. Use %resume to let the main thread start executing!
[Android Emulator 5554::owasp.mstg.uncrackable1]-> %resume
[Android Emulator 5554::owasp.mstg.uncrackable1]-> root detection method a() is called
root detection method b() is called
root detection method c() is called
debug detection method a() is called

Once we type %resume, we see our log messages. This means everything works as expected. The app doesn’t display any error messages, so we can now enter the secret message just like we did before.

--

--

No responses yet