[C2C CTF 2023] Boring Old Default
Description
Just you're stock-standard AndroidStudio starter application. Or is it? 0.0
Solution
Identify the file as an Android apk
, therefore, we need to decompile it. I used the one on javadecompilers.com, but downloading and using JADX locally has the same result.
The first thing I did was run grep
to find a flag, which quickly narrowed down the search space to one file of interest:
$ grep -r "FLAG{" .
Binary file ./resources/classes3.dex matches
./sources/com/example/boring_old_default/WeirdActivity.java: newString.append("FLAG{");
./sources/com/example/boring_old_default/WeirdActivity.java: newString.append("FLAG{a");
./sources/com/example/boring_old_default/WeirdActivity.java: newString.append("FLAG{a3");
./sources/com/example/boring_old_default/WeirdActivity.java: newString.append("FLAG{a3b");
./sources/com/example/boring_old_default/WeirdActivity.java: newString.append("FLAG{a3b... Awwwwh so close!");
Looking at WeirdActivity.java
, there were a few things of interest:
public class WeirdActivity extends AppCompatActivity {
...
static {
System.loadLibrary("images-lib");
}
...
private String handleFirstHalf() {
int[] nums = firstHalf();
StringBuilder firstHalfStr = new StringBuilder();
for (int i = 0; i < 9; i++) {
firstHalfStr.append((char) (nums[i] - 42));
}
return firstHalfStr.toString();
}
/* access modifiers changed from: private */
public String handleButtonPress() {
StringBuilder newString = new StringBuilder();
switch (this.counter) {
...
case 100:
newString.append(handleFirstHalf());
this.beepAccessed = 1;
break;
...
}
newString.append("F");
return newString.toString();
}
/* access modifiers changed from: private */
public String handleSecondHalf() {
if (this.beepAccessed != 0) {
return secondHalf(new Random().nextInt(10066329));
}
return "Not that easy pal... " + somethingSpecial();
}
It seems like the app imports a library images-lib
, which has
firstHalf()
that returns an array of ints that are to be decoded as characters.secondHalf()
that returns a string with the 2nd half of the flag.
Let’s pop the [apk dir]/resources/lib/x86/libimages-lib.so
into Ghidra.
First Half
In ghidra we navigate to firstHalf
function, and can see that
- Some memory gets copied into
local_5c
local_5c
gets copied tolocal_38
- I can assume that
loca_38
gets converted to an int array and put intolocal_60
local_60
gets returned.
Let’s look at the memory region that is copied in step 1.:
We can see that it’s 9 ints, all of which are less than 255. Put them into Python and copy what handleFirstHalf()
function does in Java:
firstHalf = [0x70, 0x76, 0x6B, 0x71, 0xA5, 0x8B, 0x5D, 0x8C, 0x90]
firstHalfStr = ""
for i in firstHalf:
firstHalfStr += chr(i-42)
print(firstHalfStr)
We get the 1st part of the flag: FLAG{a3bf
Second Half
We look at the 2nd half in ghidra and find that it does operations on a certain string "=0HMmJ2MxADZ
. It took me a while to figure out that the decompiled pseudocode is a bit wrong (as often happens with ghidra), so you must look at assembly if you want to understand the function. But you don’t need to! The string has an =
at the beginning, so it looks like a reversed base64 encoded string. So run the following in python:
import base64
print(base64.b64decode("=0HMmJ2MxADZ"[::-1]))
and you get the 2nd half of the flag: d013bf0}
Flag
FLAG{a3bfd013bf0}