Challenge Description#
This app contains some unique keys. Can you get one?
APKey.apk
Solution#
Static Analysis#
The first step is always static analysis. I started by using jadx-gui
to see the decompiled Java code. Looking into the AndroidManifest.xml
, it looks like there’s only one activity which is MainActivity
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <application
android:theme="@style/Theme.APKey"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:supportsRtl="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:appComponentFactory="androidx.core.app.CoreComponentFactory">
<activity android:name="com.example.apkey.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
|
Since it’s gonna be MainActivity
, we could have a look at the code. Inside the code, we have something interesting.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| public void onClick(View view) {
Toast makeText;
String str;
try {
if (MainActivity.this.f928c.getText().toString().equals("admin")) {
MainActivity mainActivity = MainActivity.this;
b bVar = mainActivity.e;
String obj = mainActivity.d.getText().toString();
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(obj.getBytes());
byte[] digest = messageDigest.digest();
StringBuffer stringBuffer = new StringBuffer();
for (byte b2 : digest) {
stringBuffer.append(Integer.toHexString(b2 & 255));
}
str = stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
str = "";
}
if (str.equals("a2a3d412e92d896134d9c9126d756f")) {
Context applicationContext = MainActivity.this.getApplicationContext();
MainActivity mainActivity2 = MainActivity.this;
b bVar2 = mainActivity2.e;
g gVar = mainActivity2.f;
makeText = Toast.makeText(applicationContext, b.a(g.a()), 1);
makeText.show();
}
}
makeText = Toast.makeText(MainActivity.this.getApplicationContext(), "Wrong Credentials!", 0);
makeText.show();
} catch (Exception e2) {
e2.printStackTrace();
}
}
|
Looking into this part of the code, I could see there’s a string comparison for admin
and a function that seems to be MD5 but the strings is 31 characters. Well since I have no idea, I decided to look into the toast.makeText
as it seems like the function to get flag. It’s playing around b.a(g.a())
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| public class g {
public static String a() {
StringBuilder sb = new StringBuilder();
ArrayList arrayList = new ArrayList();
arrayList.add("722gFc");
arrayList.add("n778Hk");
arrayList.add("jvC5bH");
arrayList.add("lSu6G6");
arrayList.add("HG36Hj");
arrayList.add("97y43E");
arrayList.add("kjHf5d");
arrayList.add("85tR5d");
arrayList.add("1UlBm2");
arrayList.add("kI94fD");
sb.append((String) arrayList.get(8));
sb.append(h.a());
sb.append(i.a());
sb.append(f.a());
sb.append(e.a());
ArrayList arrayList2 = new ArrayList();
arrayList2.add("ue7888");
arrayList2.add("6HxWkw");
arrayList2.add("gGhy77");
arrayList2.add("837gtG");
arrayList2.add("HyTg67");
arrayList2.add("GHR673");
arrayList2.add("ftr56r");
arrayList2.add("kikoi9");
arrayList2.add("kdoO0o");
arrayList2.add("2DabnR");
sb.append((String) arrayList2.get(9));
sb.append(c.a());
ArrayList arrayList3 = new ArrayList();
arrayList3.add("jH67k8");
arrayList3.add("8Huk89");
arrayList3.add("fr5GtE");
arrayList3.add("Hg5f6Y");
arrayList3.add("o0J8G5");
arrayList3.add("Wod2bk");
arrayList3.add("Yuu7Y5");
arrayList3.add("kI9ko0");
arrayList3.add("dS4Er5");
arrayList3.add("h93Fr5");
sb.append((String) arrayList3.get(5));
sb.append(d.a());
sb.append(a.a());
return sb.toString();
}
public static String b() {
return String.valueOf(d.a().charAt(1)) + String.valueOf(i.a().charAt(2)) + String.valueOf(i.a().charAt(1));
}
}
|
Looking into g
, there’s function a
and b
and function a
seems to be something I want but it has a lot of random functions from different class.
Dynamic Analysis#
Since there’s a lot of random function, I decided to perform dynamic analysis by using frida
to force executing the Java code. To do so, I’ll need to use objection
to patch the APK.
1
2
3
4
| PS D:\> objection patchapk -s .\APKey.apk
No architecture specified. Determining it using `adb`...
Detected target device architecture as: arm64-v8a
...
|
After patching and installing it to my devices, it should be ready to use. Since it is patched using objection
, I’ll need to use frida
to hook it to let the APK running as intended.
1
2
3
4
5
6
7
8
9
10
11
12
| frida -U -N com.example.apkey -l .\frida.js
____
/ _ | Frida 16.5.9 - 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/
. . . .
. . . . Connected to POCOPHONE F1 (id=d211a91c)
|
After hooking it, I could see the app working as intended.

This looks like it’s related to the function where it checks for admin
and a weird 31 hash. Since this is useless, I then continue with what i wanted to do which is using frida
to execute the Java function.
1
2
3
4
5
| Java.perform(()=> {
let g = Java.use("c.b.a.g");
let test = g.a();
console.log("g.a() result = " + test);
});
|
By using this frida
script, I could instantly get the result of the Java code.
1
2
| [POCOPHONE F1::com.example.apkey ]-> %reload
g.a() result = 1UlBm2kHtZuVrSE6qY6HxWkwHyeaX92DabnRFlEGyLWod2bkwAxcoc85S94kFpV1
|
This seems to be another weird strings which I have no idea what it is. I then remembered that the function is b.a(g.a())
which means this strings works as a key for b.a()
functions. I then changed my script accordingly.
1
2
3
4
5
6
7
8
| Java.perform(()=> {
let g = Java.use("c.b.a.g");
let b = Java.use("c.b.a.b");
let test = g.a();
console.log("g.a() result = " + test);
let result = b.a(test);
console.log("b.a() result = " + result);
});
|
I then get some good result from the frida script.
1
2
3
| [POCOPHONE F1::com.example.apkey ]-> %reload
g.a() result = 1UlBm2kHtZuVrSE6qY6HxWkwHyeaX92DabnRFlEGyLWod2bkwAxcoc85S94kFpV1
b.a() result = HTB{m0r3_0bfusc4t1on_w0uld_n0t_hurt}
|
Things I learned from this challenge#