Tags: stack overflow shellcode radare2 

Rating:

# CSAW '17
## pilot (pwn 75)

https://ctf.csaw.io/challenges#pilot

> Can I take your order?

> `nc pwn.chal.csaw.io 8464`

### Recon

We are supplied with a binary file along with the address to the remote server where the flag is stored. Our goal is to spawn a shell on the server and retrieve the flag which is located in the file system. Here's the output when I run the program with no inputs:
```
[*]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!....
[*]Location:0x7ffebcea5460
[*]Command:
[*]There are no commands....
[*]Mission Failed....
```
The first thing to notice is that it prints something that looks like a location on the stack. It then asks for input from the user and complains that there is none. Let's open radare2 in debug mode and see if we can figure out what that address is for...
```
$ r2 -d ./pilot
```

Let's start by disassembling main:
```
> aaa
> s main
> pdf
```
At the top, we see the layout of our stack - 1 variable, 32 bytes long:
```
; var int local_20h @ rbp-0x20
```
Skip the parts where it outputs the introduction. The first interesting part is the location output:
```
| 0x00400aa1 4889c2 mov rdx, rax
| 0x00400aa4 488d45e0 lea rax, qword [local_20h]
| 0x00400aa8 4889c6 mov rsi, rax
| 0x00400aab 4889d7 mov rdi, rdx
| 0x00400aae e8bdfdffff call sub._ZNSolsEPKv_80_870
| 0x00400ab3 be90084000 mov esi, sub._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6__96_890 ; 0x400890
| 0x00400ab8 4889c7 mov rdi, rax
| 0x00400abb e8c0fdffff call sub._ZNSolsEPFRSoS_E_88_880
```
The important part here is that we see a reference to `[local_20h]` at `0x004000aa4`. This implies that the printed address is probably a reference to our stack variable.

Continuing on, we find the input:
```
| 0x00400acf 488d45e0 lea rax, qword [local_20h]
| 0x00400ad3 ba40000000 mov edx, 0x40 ; '@'
| 0x00400ad8 4889c6 mov rsi, rax
| 0x00400adb bf00000000 mov edi, 0
| 0x00400ae0 e83bfdffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
```
This is a call to `istream::read`, at file descriptor `0` (stdin), to our stack variable `local_20h`, at a length of 0x40 bytes. This should throw a *massive* red flag. `read` is called with a length that is twice the size of our buffer! This is definitely exploitable.

Earlier, we theorized that the printed address might point to the stack variable, so let's test that before we move on. Set a breakpoint right before main returns and run the program. While it's running, go ahead and provide an input that we can recognize:
```
> db 0x00400b34
> dc
...
[*]Location:0x7ffc3dd9fe10
[*]Command:AAABAAAC
hit breakpoint at: 400b34
```
Note: Your location will be different than mine, and it will also change every time the program runs. That's ASLR doing it's job.

Alright, so we've paused the program before it jumps out of the stack frame, now we can analyze that address. Let's print the data at that location (remember to replace this address with whatever your program printed):
```
> px 64@0x7ffc3dd9fe10
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x7ffc3dd9fe10 4141 4142 4141 4143 0a08 4000 0000 0000 AAABAAAC..@.....
0x7ffc3dd9fe20 10ff d93d fc7f 0000 0000 0000 0000 0000 ...=............
0x7ffc3dd9fe30 900b 4000 0000 0000 6abf 0c3c 127f 0000 [email protected]..<....
0x7ffc3dd9fe40 0000 0000 0000 0000 18ff d93d fc7f 0000 ...........=....
```
Check it out, our input is there! That's interesting how they supply us with the address of our input, practically defeating the purpose of ASLR. Also note that on the 3rd line down, you can see the end of our stack frame, which contains the base address of the next stack frame and the return address. The return address is 40 bytes away from the start of our input, so it definitely can be overwritten with 64 bytes from the `read` call.

At this point, we have enough information to form an exploit. We have a buffer overflow capable of modifying the return address, and we are provided the address of our input which means we can store shellcode there and return to it.

### Exploit

The exploit is very simple: at runtime, we need to fetch the location of our input from the program. Then, we need to craft a payload that can drop us to a shell. We can store shellcode at the beginning of our payload, pad it to 40 bytes (the offset to the return address), and append the return address to it.

REMEMBER: We are working on a *little-endian* system, so that means the return address needs to be sent with the least significant byte first. For example, the address I received above, `0x7ffc3dd9fe10`, would be sent back as `10fed93dfc7f`. Thankfully, the `pwntools` library will take care of that for us when we're writing the script.

So initially, I grabbed some shellcode from [shell-storm.org](http://shell-storm.org/shellcode/files/shellcode-806.php), padded it to 40 bytes, appended the address I received earlier, and let it rip! It works, right? *Nope*. I get a segmentation fault. In fact, I tried several shellcodes, and none of them seemed to work.

After much headache, I opened up a debugging session to examine the situation. What happened was that when it returned to the shellcode, `rsp` was set to a location 16 bytes above the old stack variable, and approximately 19 bytes above where the shellcode started. That means I could safely push 19 bytes onto the stack before it overwrote my shellcode. Unfortunately, the shellcode I chose pushed up to a depth of *32 bytes,* overwriting the shellcode itself with illegal instructions!

To fix this, I added some of my own machine code to the beginning: `add rsp, 0x30`. This added 48 more bytes between the stack head and the shellcode, which was more than enough for the shellcode to execute correctly and drop me to a shell.

### Solution

[`exploit.py`](https://gitlab.com/AGausmann/ctfs/blob/master/2017/csaw/pilot/exploit.py) contains the solution, with hooks to a local process, the remote server, and a debugging connection to radare with the provided [rarun2 script](pilot.rr2): `r2 -dR pilot.rr2 ./pilot`. Simply edit `exploit.py` and uncomment the appropriate line starting with `rc = ...`

Flag: `flag{1nput_c00rd1nat3s_Strap_y0urse1v3s_1n_b0ys}`

Original writeup (https://gitlab.com/AGausmann/ctfs/tree/master/2017/csaw/pilot).