CSAW '17 PWN - Auir (200pt)

September 25 2017

This was a fun challenge, being the third exploit challenge in the CTF but still a great one!

The challenge description reads:

At long last, we stand at the threshold of destiny. For today, we will restore the glory of our legacy. Today, we will retake what we have lost and reclaim our homeworld. -Artanis-
nc pwn.chal.csaw.io 7713

And as usual, running checksec I see:

[*] '/home/glenn/Downloads/csaw/csaw17/auir/auir'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

So 64 bit again, NX enabled by no canary or PIE, hmm..
Let's run the binary and have a look what is going on to help find the vulnerability. We are presented with a menu system that lets us create, destroy, edit and print zealots.


Leak Libc

This absolutely screams heap exploit. So let's find the vulnerability! After playing withe the binary I quickly find a vulnerability. There is a UAF where you can leak libc addresses from the main arena. This is done by the following steps:

  1. Create two zealots with size in the smallbin range (e.g. ~150 bytes)
  2. Destroy the one with index 0
  3. Read from the one with index 0

Result being:


This happens because when we free the zealot with index 0, the skills section of the zealot (the chunk data) is overwritten with pointers into the main arena due to the smallbin array that this chunk resides in, and when we use the Display Skills option of the menu with this freshly deleted small-bin sized zealot - we leak a libc address. Woohoo! Now to do anything with this leak we will need some form of execution control.

To get the address of libc base, we simply need to find the mapped in starting address of libc using radare, and find the difference between this and the libc address within the main arena that we leaked. This offset amount will be fixed for the libc provided - and for this challenge was 0x3c4b78.

Write primitive

After playing with the binary for a while I found there was a heap overflow if you created a zealot, then used the Fix Zealot menu option to provide a new size and new data. You could then overwrite the meta data of the next chunk in the heap. I spent a while with this setting up the unlink exploit to get a write into the GOT. However after taking a browse through the assembly during a break I found a much easier write primitive!

We can actually use negative indexes for the fix_zealot function to be able to write where we want! And thank goodness there was a ptr to an address also behind the global array for the zealots, so we could write to this address the value of the GOT, and then write to the value of the GOT a few entries up, an arbitrary value.

This sounds confusing, but the gist of it was that we could use a negative index when calling the fix_zealot function to index the global zealot array in the data section backwards. This was important, because the function would then write to the address it found when it indexed the global zealot array - writing the new data we provided in the fix_zealot call. Great!

By looking behind the global zealot array, we found a reliable address which contained an address a little bit higher up. This was significant because we could use an index of -12 to reference an address, which contained a value that was at an index of -29. So the game plan was to call fix_zealot and provide the index -12 and skills of the GOT address of free. What this would do is write the GOT address of free to the address that was the same as the negative indexed location of -29. What we could then do was call fix_zealot with the index of -29 and the skills of the address of system. So that when we call free on a pointer that contains the skills of "/bin/sh/" we would actually be calling system("/bin/sh").

Does that make sense? Here is a dump of 256 bytes back from the global zealot array (which is located @ 0x605310).

[0x004009a0]> x/32xg @ 0x605310 - 256
0x00605210  0x0000000000000000  0x0000000000000000   ................
0x00605220  0x0000000000000000  0x0000000000605060   ........`P`.....
0x00605230  0x0000000000000000  0x0000000000000000   ................
0x00605240  0x0000000000000000  0x0000000000000000   ................
0x00605250  0x0000000000000000  0x0000000000000000   ................
0x00605260  0x0000000000000000  0x0000000000000000   ................
0x00605270  0x0000000000000000  0x0000000000000000   ................
0x00605280  0x0000000000000000  0x0000000000000000   ................
0x00605290  0x0000000000000000  0x0000000000000000   ................
0x006052a0  0x0000000000000000  0x0000000000000008   ................
0x006052b0  0x0000000000605228  0x00007f137e94b7a0   (R`........~....
0x006052c0  0x0000000000000000  0x0000000000000120   ........ .......
0x006052d0  0x00007f137e949820  0x00007f137e94b1c0    ..~.......~....
0x006052e0  0x00007f137e94b150  0x00007f137e94b160   P..~....`..~....
0x006052f0  0x00007f137e0a7620  0x0000000000000000    v.~............
0x00605300  0x0000000000000000  0x0000000000000000   ................

So the address of 0x006052b0 is actually the address that is an index of -12 back from the start of the global zealot array. We can check with the maths:

[0x7f1b8190dc30]> ? 0x605310 - 0x006052b0
96 0x60 0140 96 0000:0060 96 "`" 01100000 96.0 96.000000f 96.000000
[0x7f1b8190dc30]> ? 96 / 8
12 0xc 014 12 0000:000c 12 "\f" 00001100 12.0 12.000000f 12.000000

There - an index of -12 corresponds to 8 QWORDS back. And this address contains the value that is at an index of -29 from the start of the zealot array. I will save you the maths, but check if you like. So by indexing -12 and providing the GOT address of free as the skills we will be writing to the address at an index of -29. And by then indexing -29 and providing the address of system as the skills we will be overwriting the GOT entry of free with the address of system.

Pop shell

All that is left to do now is create a zealot and provide the skills as the string /bin/sh, and then destroy this zealot - which will call free on the string /bin/sh, but since the free entry in the GOT has been overwritten with the libc address of system, we call system("bin/sh"). Woohoo!

Here is the final exploit:

And let's get the flag!

↳ python auir.py
[+] Opening connection to pwn.chal.csaw.io on port 7713: Done
[*] [add_zealot] size:215, index:0
[*] [add_zealot] size:215, index:1
[*] [destroy_zealot] index:0
[*] [display_zealot] index:0
[+] Leaked smallbin addr: 0x7fe4551b5b78
[+] Leaked libc_base: 0x7fe454df1000
[*] [add_zealot] size:215, index:2
[*] [fix_zealot] index:-12, size:8
[*] [fix_zealot] index:-29, size:8
[*] [fix_zealot] index:0, size:8
[*] Switching to interactive mode
$ ls
$ cat flag