A box of chocolate

my public personal notebook

Codegate 2016 Quals BugBug

I played Codegate 2016 Quals last week with our team BabyPhD. Although I couldn't solve many tasks :(, the CTF is quite fun to play :3

We are given a nice-looking small x86 Linux binary that reads our name and let us play lotto. After loading up the binary in IDA, we found a obvious format string vulnerability: f:id:dakutenpura:20160316163854p:plain

But in order to execute this part, we have to correctly guess 6 numbers that the program generated with glibc's rand() function. After some more examination, we can see that the program reads 4 bytes from /dev/urandom to a buffer lies right after our name buffer on the stack: f:id:dakutenpura:20160316164103p:plain

After that, 6 numbers will be generated by this formula: val = rand() % 45 + 1, and they must be pairwise different. Thanks to seed's memory address on the stack, we can easily leak seed's value by filling the name buffer with some non-null character. Due to how C handles strings (a string must be terminated with a null byte), the whole seed value will be printed along with our "name" if it doesn't contain any null bytes. I used foresight to generate rand() values in Python. My code will throw EOFError sometimes when it couldn't recover the seed or somehow it can't generate the right answer, so I had to run it several times :(

After passing the lotto check, everything is quite straightforward. The program will call exit(0) after calling printf(), so we can control the instruction pointer by overwriting its entry in the GOT. Try to retrieve some stack addresses on the remote server, we can see that ASLR is turned on, so we'll need to leak some addresses first in order to know the libc version and then calculate system() address.

We can use %s to read values at a specific memory address. I'll retrieve the addresses of putchar() and rand() in libc by reading their GOT entries. Because the binary is not a PIE, all of its addresses are fixed just like in IDA. I overwrote exit() with 0x0804883D so that we won't need to calculate rand() values again. Libc's version on the remote server is the same as my libc's version (Ubuntu 15.10).

In the second stage, I overwrote printf() with the address of system(), supply /bin/sh in the format string and then overwrote exit() to call system() in order to get shell. Thanks to the binjitsu library, I can generate format string payloads faster :3

Here is my code:

gist.github.com

The flag file stays in /home/bugbug/, and the flag is SOur_cr3am&oni0n.