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:
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: