How to Win A CTF by Overcomplicating Things

My unnecessary long writeups for Petronas Inter-University CTF 2023

Step 1: Find very good teammates

Let them do all the work.

Step 2: Spend the whole competition looking for unintended solution

Act busy by going the long way to solve challenges with unintended solutions.

Qualifiers

ReverseMe (Android Reversing)

In the first Android Reversing challenge, we were given the file myapps.apk, which can be decompiled with apktools or decompiler.com.

Intended Solution

To get the flag the easy way, perform a simple recursive grep.

Unintended Solution

To make my life easier, I decided to install the package on a Pixel 5 emulator using Android Studio to interact with the Android application.

Overall, the application presents an event handler that loads the flag in the background when the button is pressed.

Taking a look at the source code, the flag is used in the displayFlagStatus method to calculate its length, but was never printed out on the application interface.

A novel method to leak the flag is by patching the application's Dalvik bytecode. The assembly representation of displayFlagStatus is a relatively big chunk, but our main interest is only .param p1:

Drawing

To retrieve the flag during runtime, we'll make use of the PrintStream object from Java's IO class. The remaining portion of the code can then be discarded safely.

Once the changes have been made, we can recompile the application using APKLab, and re-install it on the emulator with adb.

Up until this point, we're still missing 1 crucial step. Think about it; we are printing the flag to the console, but there's no direct access to the terminal via the application's interface. To overcome this, we can use adb logcat to dump system logs, which include messages written from our application's code.

This is actually one of the common pitfalls in mobile development dubbed "Insecure Logging", whereby sensitive information logged in a staging environment is shipped to production.

Flag: {PETGRAD2023}_S1mPl3Fl4g

GetMeCorrect (Android Reversing)

We have yet another Android Reversing challenge with the given file dynamic.apk. As usual, we'll decompile the application to look at some point of interest.

Drawing

It appears that the flag is constructed part-by-part with a string builder during runtime, with part 3 derived from unknown native libraries.

Intended Solution

Part 1, 2, and 4 of the flag is visible directly from LiveLiterals$MainActivityKt.java:

Drawing

Since part 3 is derived from native libraries, a simple strings command on the decompiled library should reveal it. Putting everything together would then give us the complete flag.

Unintended Solution

After installation, the original APK file appears to be corrupted, which results in runtime error. Not entirely sure if this is by design or accidental, but we'll try to repair the APK so that it actually runs.

After several troubleshooting attempts, the following modifications on AndroidManifest.xml are required:

  • android:extractNativeLibs="false" -> android:extractNativeLibs="true"

  • android:theme="@style/Theme.DynamicFlagChallenge" -> android:theme="@style/Theme.AppCompat.Light"

Furthermore, the application seems to be loading the native library via the wrong file name. To fix this, I simply renamed libdynamicflagchallenge.so to libnativeFlagPart.so and recompile with APKLab.

After getting the application to run, we are greeted with a similar interface as the previous challenge.

The popup message Nice try! But you'll have to dig deeper is implemented through the Toast class whenever the button is pressed. Similar to the previous challenge, we'll tackle this challenge by patching Dalvik bytecode.

Drawing

This time around, instead of printing the flag to a console terminal, we'll make use of the existing Toast class for the same purpose. The patched code is going to be slightly lengthy as we need to retrieve all 4 parts of the flags and concatenate them together to form the final flag.

Recompile the application and press the button to get the final flag!

Flag: petgrad2023{Qu@ntum_N3xu$_C0d3br3@k3r}

Finals

We’re Running Out of Time! (Firmware Reversing)

For this challenge, we were given an Atmel AVR firmware runningoutoftime2.hex in the Intel Hex format.

The aim is to derive the 5-digit PIN by any means and unlock the physical safe located in the middle of the room.

Unintended Solution

To emulate the firmware logic, I opted to use the following simavr components:

  • simduino to boot start the Arduino emulator.

  • picocom for serial input.

  • avr-gdb for debugging.

Drawing

The physical safe itself has a lockout policy to prevent brute forcing during the CTF. However, the firmware binary does not contain this constraint when emulated with simavr. As shown above, simduino exposes /dev/pts/3 for serial input and /dev/pts/4 for serial output. This presents a perfect opportunity for us to brute force the PIN as there are only 99999 combinations to try.

Drawing

We can automate this whole process using input & output redirection magic in bash.

Intended Solution

The AVR firmware provided initially did not contain any symbols, which makes debugging a little difficult. Near the end of the CTF, the author released the original ELF file that contains all the debugging symbols.

Disassembling dbg.main under Radare2 reveals an interesting loop of comparison statements where our input PIN is checked. Since the correct PIN is loaded into a couple of registers, a simple registers dump should give us the answer.

Enable OPCODE description with e asm.describe = true in Radare2.

Using GDB, place a breakpoint on the first brne instruction and dump all registers value with info registers. In this case, the correct PIN for each position is stored on the lower byte region; whereas our input is stored on the higher byte region.

Position
Input
Correct PIN

Pos 1 (r18-r19)

5

8

Pos 2 (r20-r21)

4

9

Pos 3 (r22-r23)

3

0

Pos 4 (r30-r31)

2

7

Pos 5 (r26-r27)

1

6

Drawing

PIN: 89076

Intruding the Ominous Black Cube (Network)

This challenge is worth 700 points, making it the highest-score challenge (but not necessarily the hardest) throughout the entire CTF. To start off, we were given a relatively small packet capture file blackcube.pcap, containing only 295 packets to analyze.

Initial analysis reveals several endpoints /admin, /menu, /dashboard made to the web server on port 5000, but nothing too interesting, except for packet 205, which reveals a suspicious base64 encoded string.

Decoding the string gives us petronasgradctf:Whoistheg0dofCtf that looks like a set of credentials we could use somewhere else.

Looking at the list of network access points, we were able to authenticate to a hidden network using the credentials above.

Later on, I was stuck at this challenge until a dead giveaway hint was released afterward.

The hint combined with a bunch of TCP retransmission confirmed that there's something to do with Port Knocking. Coincidentally, a topic that I've shared previously for a sharing session.

Now we'll have to find out the knock sequence by figuring out the timeline where the web server responded to our request on port 5000 after the TCP retransmission stream ends.

Sending the knock sequence is pretty straightforward. The flag -d 500 is used here to introduce delay between knocks, just in case the server receives them in the wrong order.

Navigating to port 5000, we can now access the web server which wasn't possible before.

Recall that the endpoint upload-page was found during PCAP analysis, and the subtle hints of TAR file made it obvious that we needed to exploit a TAR Wildcard Injection vulnerability. (From OSCP lol, which I took just a week before the CTF)

Prepare the payload as follows:

Finally, upload the tar file to receive the flag.

Flag: petgrad2023{7h3_B1@ck_cu83}

Step 3: Free-load the trophy

Last updated