Blog of Malte Kraus

home

CSAW CTF Finals: "DEFCON 1" and "Global Thermonuclear Cyberwar" challenges

18 Nov 2017

Last week, I and 3 other members of FAUST got to travel to Valence, France for the finals of the CSAW CTF. In the end, we didn't place too well, but it was still great fun. This post is about the two challenges Johannes and me worked on for most of the contest, but did not manage to finish in time.

DEFCON 1

For the "DEFCON 1" challenge, a 16bit boot ROM for x86 was provided (like in the "realism" challenge from the qualifiers). When booting this image in Qemu, it asked for a password before it'd do anything else:

password prompt

So it was time to look at the code statically. We'd already found out for the qualifiers that the code from the MBR is loaded at address 0x7c00. There was not much code visible here. First some BIOS calls to setup the video mode, print the text to the screen, and read some data from disk (we guessed correctly that this just read the part of the image that is not part of the MBR and therefore is not loaded by the BIOS). Then a loop that would read 8 characters (i.e. the password) from the keyboard.

This was followed by the actual challenge: a loop XORed the rest of the image with the (repeated) password and the static value 0xc3. When the first few bytes decrypted to the text WARGAMES, the code jumped into the decrypted area, otherwise it would ask again for the correct password.

Me managed to waste a surprising amount of time on off-by-one errors and the missing 0xc3 key, but in the end found the correct password: -JOSHUA-. When typing this into the running system, a game was unlocked where you could play either as the US or the USSR and drop some bombs on the enemy.

It turns out that is pretty much all there is to the game, there is no winning and no destroying things owned by the enemy.

For the first 50 points, however, playing the game was not necessary. We wrote a quick Python script to decode the image in order to analyze it statically and found the embedded flag as a string: flag{ok_you_decrypted_it_now_plz_pwn_it!}. Directly after that followed the text flag{__PWN_ON_SERVER_TO_GET_REAL_FLAG__}, telling us about the address of a second flag on the CSAW servers for the pwning challenge:

Global Thermonuclear Cyberwar

The same ROM image also could be pwned. On the server side, a game storing a different flag was running, so the goal was to get this string, by only playing the game - without access to the image running there.

We wasted a lot of time reversing the whole binary (looking for non-obvious features of the game) because we could not find the bug, when a bit (more) dynamic analysis would have revealed the bug: when printing the flight path of the missiles (see video above), one could trigger a buffer underflow when this path did not fit into the screen. The frame buffer was located at an address higher then the rest of the memory, so when manually triggering such an underflow, the game would often crash.

Once we knew about the bug, the feature that allowed choosing the color also made perfect sense: the color is the byte value that's written into the frame buffer, so by changing the color we can determine what to write to memory. However, there was still the problem that we didn't really know which addresses would be overwritten for every set of bases attacking each other. Our attempts with python scripts for gdb (attached remotely to qemu) failed due to performance, and the emulation/binary analysis frameworks we were familiar with didn't support 16bit x86. The code that was computing the flight trajectories was also too complicated for us to re-implement during the CTF.

In the end, I manually patched the binary. I wrote some assembly that iterated over all pairs of enemy/friendly bases and called the function to print the trajectory. I also patched this function so that, instead of writing to the frame buffer, it would write the touched address to the serial console, using a BIOS call. Running that modified image, we got a list of all the memory addresses touched for each pair of bases.

During the CTF, I mistakenly believed that we'd have to print the individual bytes of the flag in unary because writing text using BIOS calls would require switching the video mode, for which there was no existing code in the ROM. That, however, was wrong. A successful exploit 'just' needed to call the function that prints the welcome message, after patching this function so that it would print a string at a different address (i.e. the flag instead of the welcome message). The assembly for this is 9 bytes:

org 0xf070
mov word [0x7c14], 0x1664
jmp 0x7c00

As can be seen from that snippet, we found a long enough consecutive memory area we could write to at address 0xf070. After writing the instructions there, we need to overwrite a return instruction pointer on the stack in a single write - luckily there's one ending in 0x70, so that writing 0xf0 to the stack address 0xefff executes our code, and prints the flag.

The final exploit looks like this in action: