Description
Two heap allocations sit adjacent in memory. Overflow the first to corrupt the second and redirect execution to print the flag.
Setup
Connect to the challenge server.
Analyze the binary to understand its heap allocation pattern.
nc <host> <PORT>Solution
- Step 1Map the heap layoutUse GDB with pwndbg to visualize the heap. Identify the two malloc'd chunks and their sizes. The distance between the start of the first buffer and the start of the second is the overflow offset.
gdb ./binaryheap chunksLearn more
The heap is a region of process memory used for dynamic allocations via
malloc()/free(). Unlike the stack, which grows and shrinks in a last-in-first-out order, the heap is managed by an allocator (glibc's ptmalloc on Linux) that tracks free and allocated regions using metadata structures called chunks.Each heap chunk has a header (16 bytes on 64-bit systems) that stores the chunk's size and status flags. The usable data starts immediately after this header. When two
malloc()calls are made in sequence with no intervening frees, the resulting chunks are typically adjacent: the second chunk starts atchunk1_start + chunk1_size + header_size. Overflowing the first buffer's data region can thus reach into the second chunk's header or data.pwndbg is a GDB plugin that enhances heap inspection. The
heap chunkscommand displays all allocated and free chunks with their addresses, sizes, and contents - making the layout immediately visible without manual memory arithmetic. - Step 2Determine the target valueThe second allocation holds a function pointer or command string. Decide what value to write there - typically a win function address or the string '/bin/sh'.
Learn more
Function pointers stored in heap memory are attractive overflow targets because overwriting them with a different address redirects execution when the pointer is called. A common pattern in CTF challenges is a struct where one member is a character buffer and another is a function pointer - overflowing the buffer overwrites the pointer with the address of a
win()function that prints the flag.To find the address of the target function, use GDB:
p winorinfo functions win. If the binary has no PIE (Position Independent Executable), addresses are fixed and can be read directly from Ghidra orobjdump -d binary | grep win. With PIE enabled, a leak of the binary base address is required first.Alternatively, the second buffer might hold a system command string (like
lsordate) that is passed tosystem(). Overwriting it with/bin/sh\x00turns the call intosystem("/bin/sh"), spawning an interactive shell. The null byte terminator is important - without it, the string may continue past the intended characters and confuse the shell. - Step 3Send the heap overflow payloadSend padding equal to the heap distance followed by the target value. The second allocation is overwritten, triggering the flag output.
python3 -c "from pwn import *; p=remote('<host>',PORT); p.sendline(b'A'*<offset>+b'/bin/sh\x00'); p.interactive()"Learn more
The heap overflow payload follows the same structure as a stack overflow: padding to reach the target, then the value to write there. The critical difference is that the offset is determined by the heap allocator's chunk layout rather than the stack frame layout. Chunk headers and alignment padding (the allocator rounds sizes up to multiples of 16) mean the actual distance can be slightly larger than the logical buffer size.
Heap exploitation is a rich field with many advanced techniques beyond simple buffer overflows: use-after-free (UAF), double-free, tcache poisoning, house-of-force, and others. This challenge represents the simplest form - a linear overflow into an adjacent chunk - which builds intuition for the heap's memory layout before tackling those more complex primitives.
In modern systems, heap metadata corruption is detected by allocator integrity checks (e.g.,
malloc_consolidate()verifies chunk sizes). Challenges that allow clean data overwrites without touching chunk headers bypass these checks, while more advanced exploitation requires careful manipulation of metadata to avoid triggering allocator assertions.
Flag
picoCTF{...}
Heap overflows differ from stack overflows in that the target is adjacent malloc'd memory - chunk headers and allocator alignment determine the exact offset.