Description
Just pwn this heap challenge. Connect with nc to get the flag.
Setup
Download the binary, libc, and connect to the server.
wget <url>/sice_creamwget <url>/libc.so.6chmod +x sice_creamnc <HOST> <PORT_FROM_INSTANCE>Solution
Walk me through it- Step 1Understand the heap menuRun the binary locally. It is a heap-based challenge with options like: create ice cream (malloc), eat ice cream (free), and read flavors (read). Understand the data structures used and look for a use-after-free or heap overflow vulnerability.bash
./sice_creambashchecksec sice_creamLearn more
Heap exploitation targets the dynamic memory allocator (glibc malloc). Common vulnerabilities: use-after-free (accessing freed memory), double-free (freeing the same chunk twice), heap overflow (writing past the end of an allocated chunk). These can corrupt allocator metadata (size fields, free lists) to redirect future allocations.
Run
checksecto identify protections: ASLR, PIE, NX, RELRO, Stack Canary. These affect which exploitation techniques are feasible. - Step 2Identify the vulnerabilityAnalyze the binary in Ghidra. Look for: index bounds checking (or lack thereof), double-free potential, use-after-free in the read/write functions. The vulnerability is likely a tcache poisoning setup.bash
ghidra sice_cream &Learn more
Heap chunk layout (glibc, 64-bit). Every malloc'd chunk has a 16-byte header. Once freed and placed in tcache, the first 8 bytes of user data become the
fd(forward pointer) to the next chunk in the same-sized bin. The allocator does not validatefdon tcache pop until glibc 2.32 added pointer mangling.Allocated chunk: Freed chunk in tcache (pre-2.32): +-----------------+ +-----------------+ | prev_size (8) | | prev_size (8) | +-----------------+ +-----------------+ | size | A|M|P | <- meta | size | A|M|P | +-----------------+ +-----------------+ | user data ... | | fd -> next | <- attacker target | ... | | (rest is junk) | +-----------------+ +-----------------+tcache poisoning recipe:
x = malloc(0x20); free(x);-- x lands in tcache[0x20]. Itsfdis NULL (single entry).- Trigger UAF or overflow to overwrite
x->fdwith&__free_hook(or any 8-byte writable target). malloc(0x20)returnsx-- tcache pops the head, advances head tox->fd.malloc(0x20)again returns&__free_hook. Whatever you write into the "chunk" lands at__free_hook.- Write a one_gadget address into
__free_hook. Nextfree()jumps there with rdi pointing at the chunk being freed -- shell.
The libc base leak (step 3) is the prerequisite:
__free_hook,__malloc_hook, and one_gadgets all live at fixed offsets within libc, so once you know the libc base you know all of them. - Step 3Develop the exploitWrite a pwntools exploit script. Leak a libc address (via unsorted bin or stdout), compute the addresses of __free_hook or __malloc_hook, poison the tcache to overwrite the hook with a one-gadget, then trigger it.python
python3 << 'EOF' from pwn import * # Load binary and libc elf = ELF('./sice_cream') libc = ELF('./libc.so.6') # Start process or connect to remote p = process('./sice_cream') # p = remote('<HOST>', <PORT>) # --- Exploit skeleton --- # 1. Leak heap/libc address # 2. Overwrite tcache fd # 3. Allocate to arbitrary address # 4. Write one-gadget over __free_hook # 5. Trigger free() to get shell p.interactive() EOFLearn more
Leaking libc. If you can free a chunk into the unsorted bin (size > 0x408 or all tcache slots full), its
fd/bkpoint atmain_arena+88inside libc. Reading the chunk back via the menu's "view" option discloses that pointer. Subtract the known offset ofmain_arena+88within the provided libc to compute the libc base:leaked = u64(p.recv(8)) # main_arena+88 libc.address = leaked - 0x3ebca0 # offset of main_arena+88 in libc.so.6 free_hook = libc.sym['__free_hook'] one_gadget = libc.address + 0x4f432 # check constraints with one_gadget toolOne-gadget constraints. Each one_gadget candidate has constraints like
[rsp+0x40] == NULLorr12 == NULL.__free_hookis called ashook(ptr), so when the gadget firesrdiis the chunk pointer. Pick a gadget whose constraints are satisfied at__free_hookentry, or trigger via__malloc_hook(called with size in rdi) if those constraints fit better.
Flag
picoCTF{...}
Exploit a heap vulnerability (likely tcache poisoning) to overwrite __free_hook with a one-gadget and get a shell.