lockdown-horses picoMini by redpwn Solution

Published: April 2, 2026

Description

Horses are locked down.

Connect to the challenge server with netcat.

Download the binary and any accompanying source for local analysis.

bash
nc <challenge_host> <PORT_FROM_INSTANCE>
bash
wget <challenge_url>/lockdown-horses  # binary for local analysis
  1. Step 1Analyze the custom allocator in Ghidra
    Load the binary into Ghidra and identify any custom memory management routines - functions named alloc, free_chunk, or similar, that operate on a custom heap region rather than calling libc malloc/free. Map out the internal chunk structure.
    bash
    checksec --file=lockdown-horses
    bash
    strings lockdown-horses | grep -i 'alloc\|free\|chunk\|horse'
    bash
    gdb -q ./lockdown-horses
    Learn more

    Custom allocators are commonly used in CTF heap challenges to avoid the security checks present in modern glibc (tcache key checking, double-free detection, etc.). The custom allocator maintains its own freelist of memory chunks, often in a static buffer. Its chunk metadata (size, in-use bit, next/prev freelist pointers) is typically simpler than glibc's and may lack integrity checks entirely.

    In Ghidra, look for a global array used as a backing store and functions that partition it into chunks. The freelist is usually a global linked list head pointer. Understanding the exact layout of a free chunk's header is essential - you need to know the offset of the forward pointer to corrupt it.

  2. Step 2Find the overflow or UAF in the allocator
    Interact with the program to trigger all its menu options. Look for operations that allow writing past a chunk boundary (heap overflow) or that let you use a pointer after it has been freed (UAF). Use GDB to watch memory as you trigger these operations.
    Learn more

    Heap overflows in custom allocators are especially powerful because there are no inline metadata integrity checks (unlike glibc's chunk size field verification). An overflow from chunk A into chunk B's header lets you corrupt B's size or freelist pointers, which the allocator will trust blindly on the next free or alloc.

    Use-After-Free in a custom allocator means the freelist pointer in the freed chunk is now at the same address as a live application object. If the application lets you read from a freed slot, you can leak the freelist pointer (which reveals heap addresses). If it lets you write, you can overwrite the freelist pointer to redirect future allocations.

    In GDB, use watch *addr to set a hardware watchpoint on suspicious memory locations - execution will pause whenever that address is written to, revealing the exact code path responsible.

  3. Step 3Corrupt the freelist for arbitrary write
    With a corrupted freelist pointer, allocate chunks until the allocator returns your target address. Write a shell command or shellcode there, or overwrite a function pointer. Use pwntools to automate the exploit remotely.
    python
    python3 exploit.py
    bash
    # Template:
    python
    from pwn import *
    bash
    p = remote('<host>', <PORT_FROM_INSTANCE>)
    bash
    # Step 1: trigger UAF/overflow
    bash
    # Step 2: corrupt freelist pointer -> target_addr
    bash
    # Step 3: alloc again to get chunk at target_addr
    bash
    # Step 4: write payload
    bash
    p.interactive()
    Learn more

    The exploitation pattern mirrors glibc tcache poisoning but applied to the custom freelist. After overwriting a chunk's next pointer with an arbitrary address T, the next two allocations of the same size return: first the corrupted chunk itself, then a chunk at address T. Allocating the second chunk gives a write primitive at T.

    Useful targets for the write primitive: a function pointer stored in a global (such as a handler table or a vtable), a GOT entry (if not full RELRO), or the saved return address of a stack frame. Writing the address of system to a function pointer that is later called with user-controlled data completes the exploit.

Flag

picoCTF{...}

Custom heap allocator with no integrity checks - find the overflow or UAF, corrupt the freelist forward pointer to redirect a future allocation to a target address, then overwrite a function pointer with system.

Want more picoMini by redpwn writeups?

Useful tools for Binary Exploitation

Related reading

What to try next