HacktivityCon CTF

HacktivityCon CTF Mobile Writeup

03 August 2020

By Jacob Pimental


Last week was HacktivityCon, running from Wednesday to Friday. While I was not able to find the time to watch the talks, I did manage to participate in the CTF and complete most of the mobile Reverse Engineering challenges.

MobileOne

The first challenge was fairly simple, a basic application that displayed an image of MobilOne motor oil.

MobileOne Android CTF Challenge

My first instinct was to unpack the apk file given to us using apktool. You can do this with the command:

apktool d mobile_one.apk

After that, I spent some time looking through the source code in JADX-GUI to find a hardcoded flag or something that would point me in the right direction. Nope, all the application did was create an activity that would display the image of motor oil. Out of desperation, I went to the resource section and grep’d for the string “flag” and got the answer:

Using grep to retrieve flag

A lot easier than I originally thought. On to the next one.

Pinnochio

The second challenge was a bit more interesting. We are presented with a form that asks us to input a pin. If we put in a random number we will get a screen that says “Invalid pin”. Grepping for the pin in the resources doesn’t work this time either, so we will have to look at the source code for this one.

Pinnochio Android CTF Challenge

After unpacking the apk with apktool and reading the AndroidManifest.xml file, we know that the main activity of the application is at com.congon4tor.pinocchio.MainActivity, which sets an onClickListener that will transfer our pin to the FlagActivity when the submit button is pressed. The FlagActivity appears to be creating a hashmap with our pin and sending it as a JSON string over to http://jh2i.com:50029. I decided to verify this using mitmproxy as well.

Using itmproxy to capture Android traffic

The response for this was “Invalid pin”

Invalid PIN request sent

Since the pin is only 4 characters long we could easily brute force that with a python script. I used multithreading to speed up the process because I am impatient.

import requests
import json
from concurrent.futures import ThreadPoolExecutor

url = 'http://jh2i.com:50029/'
d = {}
headers = {'User-Agent':'Dalvik/2.1.0 (Linux; U; Android 9; Android SDK '\
           'built for x86 Build/PSR1.180720.012)',
           'Content-Type':'application/json'}

def send_request(i):
    data = {'pin': f'{i:04}'}
    r = requests.post(url, data=json.dumps(data), headers=headers)
    if not r.text == 'Invalid pin':
        print(i)
        print(r.text)

pins = [i for i in range(9999)]
with ThreadPoolExecutor() as executor:
    r = executor.map(send_request, pins)

After a few minutes, we got the pin and our flag.

Pinnochio flag

Just Not Interesting

The last challenge that I will go over was a lot more interesting than the title suggests. The application asks for a username and password and will print Invalid Credentials when you input the incorrect combo.

Just Not Interesting Android CTF Challenge

Going to the MainActivity in the source code, which you can find the same way we did for Pinnochio, the first thing that stuck out to me were the two native functions that were defined, checkUsername and checkPassword. The “native” keyword tells us that these functions are not going to be written in Java, but are instead using a native library written in ARM, x86, or another architecture. At the bottom of the MainActivty class, we can see the call to System.LoadLibrary which will load the native library “native-lib”.

Native Functions in Android

System.LoadLibrary function

The native library code for this is located at lib//libnative-lib.so. You can see there is a folder for ARM libraries and x86/x64 libraries. Since I am most familiar with x86, I decided to reverse that one. Normally I would load the so file in radare2, but I have been using Cutter on and off recently and wanted to use it to solve this challenge. For those that don’t know, Cutter is the GUI version of radare2. It can do most things that radare2 can via its sleek UI and has a console where you can input more complex r2 commands for things it cannot do.

After loading our shared library into Cutter, we can see the functions sym.Java_com_example_justnotinteresting_MainActivity_checkPassword and sym.Java_com_example_justnotinteresting_MainActivity_checkUsername which defines the native functions we saw in the apk. Starting with the checkUsername function you can see it makes a call it 0x6bf, pops the return address into ebx, and adds it by 0x1911. This is used as a base to reference other variables. For example, we can see further in the code that we subtract that base by 0x17a8 and use that value as a parameter to strcmp. If we look at the value of [0x6bf + 0x1911 – 0x17a8] (0x828) we can see the string “admin”, which must be the username for the application. Another interesting aspect of Native functions is their use of JNIEnv, which is an array of pointers to JNI functions. It is normally the first argument for a native function and is used to call other native functions. In the checkUsername function we see a call to [ecx + 0x2a4], ecx being the location of the JNIEnv. There is a handy chart that will tell you what each offset points to, this specific one is GetStringUTFChars. So this is converting our input into an array of UTF-encoded bytes.

Reverse Engineering Native Android Library

Looking at the checkPassword function we can see it calculates the value to that base offset again by calling 0x711 and adding 0x18bf to it. The function then gets the string “NOTFLAG(the_fLag_ISN’T_here!!!!)” by subtracting the base by 0x17a2. It then grabs another variable at the offset 0x1781, which appears to be an array of bytes, and XORs our input against that. It then compares to see if the XOR’d data is equal to “NOTFLAG(the_fLag_ISN’T_here!!!!)” using strncmp and will return if they are equal. It is easy enough to find the correct input using another simple Python script:

key = [40,35,53,33,55,44,38,
       81,22,13,58,62,57,32,
       8,19,43,37,54,17,78,
       58,43,13,23,23,22,85,
       72,79,70,84] # Byte array at offset 0x1781

check = "NOTFLAG(the_fLag_ISN'T_here!!!!)"

for c, k in zip(check, key):
    print(chr(ord(c) ^ k), end='')

This will print out the flag “flag{maybe_a_little_interesting}”.

Conclusion

I did not have enough time to look at the final mobile challenge that was released on the second day of the CTF. These challenges were good practice in mobile reverse engineering and I learned some things from them. A resource I found useful when going through these challenges was Maddie Stone’s Android App Reverse Engineering 101 course, specifically the chapter on native libraries and JNI functions. I recommend checking out that resource if you haven’t already. If you have any questions on how I solved the challenges or would like to share how you solved them, feel free to reach out to me on my Twitter or LinkedIn.

Thanks for reading and happy reversing!

Android, Radare2, Cutter, CTF, HacktivityCon

More Content Like This: