In this write-up I will describe how I managed to solve the Android challenge of the Google-CTF 2020 event.
The challenge is basically an APK to download with a flag inside it. First thing I like to do is to install and run the APK in an emulator using the AVD manager (Android virtual device) of Android Studio. So after I launched the app a simple activity showed up which looks like this:
I started to play with by entering different texts and all that showed is a big red "X". Usually after that part, I try to decompile the APK and analyze its code and resources, so I used Jadx to decompile the dex code of the APK ( you can use any other dex decompiler). By looking at the activity source code in Jadx, I could see only parts of the code and some weird errors about decompile failures of specific parts. One of the missing parts was the "OnClick" method of the Button that is used to submit the flag:
I understood I would have to find another way to find the flag.
I decided to look deeper at this APK, so I used apktool to decompile the dex code and unpack the resources. I started to examine the smali code that apktool generated from the dex file and I found that the activity name is a special character:ő. After spending some time looking at the smali code of the activity,all I understood was:
- The flag length must be 48
- There is a loop which takes a single character from the flag,cast it to long and shift it.
- The final check is a comparison of 2 longs, not 2 strings
- There is a weird methon in R.java that returns an array of longs that is used at the final comparison
In order to patch the smali code, I needed to recompile the apk using apktool with my patch but firstly I wanted to check I can just recompile the APK without any modifications. At my first try, apktool successfully builds the APK ,BUT there is this warning:
/ctf/google-2020/android/reverse/AndroidManifest.xml:3: Tag <activity> attribute name has invalid character 'Q'.Ignoring...
This is optional, but I prefered to replace this special character with 'M'. apktool might fail to recompile your apk due to illegal resource name - '$ic_launcher_foreground__0.xml', what I did was to remove it, then I had to remove it also from the public.xml at 'res/values/public.xml', and remove the 'android:fillColor="@drawable/$ic_launcher_foreground__0' from 'ic_launcher_foreground.xml.
Now we can patch the smali and recompile the APK without any problems. So after examine the Jadx decompile error (look at the image above) and examine the smali for a prettly long time I found that there are some extra catch blocks with a bad type: ő.smali:line 17 - catches I = int which is not possible so I deleted it.
.catch I {:try_start_0 .. :try_end_0} :catch_1
ő$1.smali:line 56 - catches J = long which is not possible so I deleted it.
.catch J {:try_start_0 .. :try_end_0} :catch_0
ő$1.smali:line 83 - this try scope doesn't have a catch handler so I deleted it.
.catch Ljava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_0
recompile again the APK after patching the smali files and Voila - we can finally see the code of the "onClick" method.
Copy the code of the onClick method into a new fresh project and debug it easly. At first, I really tried to track this code , and even look at the function in R.smali, but I couldn't really follow all these maths operations. The code takes 4 characters at a time and run this math operations, so I decided to brut-force using 4 loops to try all the possiblities from 33 to 127 (ascii). Every computed value is compared to a long in value in a 12 length long array,so all I had to do is to run this code in another loop of 12 iterations to compare each long in the array:
final int i = 0;
for (char j = 32; j < 127; j++) {
for (char j1 = 32; j1 < 127; j1++) {
for (char j2 = 32; j2 < 127; j2++) {
for (char j3 = 32; j3 < 127; j3++) {
f0M[i] = j3 << 24;
long[] jArr = f0M;
jArr[i] = jArr[i] | (j2 << 16);
long[] jArr2 = f0M;
jArr2[i] = jArr2[i] | (j1 << 8);
long[] jArr3 = f0M;
jArr3[i] = jArr3[i] | j;
if (((fun(f0M[count], 4294967296L)[0] % 4294967296L) + 4294967296L) % 4294967296L != f1class[count]) {
} else {
Log.d("zachinio", "" + j + j1 + j2 + j3);
return;
}
}
}
}
}
After some time the brut-force worked and the flag revealed in the logcat :
CTF{y0u_c4n_k3ep_y0u?_m4gic_1_h4Ue_laser_b3ams!}