Lion Cipher Lab
Poison the tcache freelist past safe-linking (in your own lab)
- Penetration Testing / Red Team
- Advanced
- ~90 min
- Free · no account required
Why it matters
The use-after-free lab taught the safest heap primitive: overwrite an application field of a chunk malloc had already handed back, so safe-linking never mattered. That was on purpose -- it kept the first heap exploit deterministic. This lab crosses the line that lab drew. Instead of writing to a chunk's payload, you corrupt the tcache free-list's own forward pointer, so the allocator itself becomes your weapon: poison one fd and the next-but-one malloc of that size returns a chunk sitting on ANY address you chose -- a global, a function pointer, a GOT entry -- giving you an arbitrary write…
What you'll learn
- tcache poisoning as a primitive: corrupting a freed chunk's fd ('forward') pointer so the tcache free-list's next entry points at an attacker-chosen address, then popping a chunk AT that address with an ordinary malloc()
- turning tcache poisoning into an arbitrary WRITE: two same-size malloc()s after a poisoned fd return a chunk overlapping a target you name, and writing to it writes wherever you aimed
- defeating glibc 2.32+ SAFE-LINKING by hand: computing the mangled fd PROTECT(pos, ptr) = ((pos >> 12) XOR ptr) yourself, and understanding why this makes a HEAP-ADDRESS LEAK a hard prerequisite (you cannot forge a valid fd without knowing the chunk's own address)
- respecting glibc 2.29+ tcache integrity checks: why the per-bin COUNT forces you to free two chunks (not one) to pull the poisoned chunk out with a second malloc, and why the aligned_OK check forces your target address to be 16-byte aligned
- reading the tcache in a debugger with pwndbg/GEF: using `tcachebins` to watch a chunk enter the 0x30 bin, see its fd stored MANGLED, and confirm the demangled 'next' now points at your target
- landing the write on a real control target -- overwriting a global function pointer in the program itself (because __free_hook was removed in glibc 2.34) and calling it to hijack control flow deterministically under -no-pie
- the judgment: WHEN this arises (a heap overflow into a freed chunk, or a use-after-free WRITE that reaches the fd), WHY safe-linking + alignment checks raise the bar without closing the hole, and how the class is actually killed (hardened allocators, ASan, memory-safe languages)
Steps in this lab
- Read this first: your own container, your own program, authorized-only
- Where this picks up: the fd pointer the last lab refused to touch
- Safe-linking: why a raw fd write dies, and why you need a heap leak
- Pin glibc with Docker -- heap exploits are version-specific
- Write the vulnerable target -- a UAF-writable slot and an aligned function pointer
- Compile with mitigations off for teaching -- and note what stays ON
- See safe-linking with your own eyes in pwndbg (tcachebins)
- Fire the exploit: poison the fd past safe-linking, land the write, call win()
- Do it better: how the class is actually killed -- and where it goes next