Overview
The tcache stashing unlink attack exploits the mechanism by which glibc moves chunks from the smallbin into the tcache. When tcache bins have room (< 7 chunks), glibc will “stash” smallbin chunks into the tcache. By corrupting thebk pointer of a smallbin chunk, an attacker can:
- Make malloc return an arbitrary pointer
- Write a libc address to an arbitrary location
This technique leverages the
tcache_stashing functionality where smallbin chunks are transferred to tcache bins when allocating from smallbins. The stashing process uses unlink-style operations that can be exploited.Glibc Version Compatibility
Works on: glibc 2.27 through 2.31+ (tested on 2.27, 2.29, 2.31) Requirements:- Tcache must be enabled (glibc >= 2.26)
- Must use
calloc()at least once (calloc bypasses tcache initially) - Need a writable address to pass
bck->fd = bincheck
What This Technique Achieves
1
Create smallbin chunks
Allocate and free chunks to populate the smallbin
2
Corrupt victim->bk
Overwrite the bk pointer of a smallbin chunk to point to a fake chunk
3
Trigger stashing with calloc
Use calloc() to trigger tcache stashing from smallbin
4
Get arbitrary pointer and libc write
The fake chunk is stashed into tcache, and a libc address is written to
fake_chunk->bk + 0x10Key Concepts
Tcache Stashing Mechanism
When tcache has space and you allocate from smallbin, glibc will:- Remove chunks from smallbin
- Place them into tcache (“stashing”)
- Continue until tcache is full (7 chunks) or smallbin is empty
Why calloc()?
calloc() is special because it bypasses tcache initially and goes directly to smallbin/largebin. This is necessary to trigger the stashing behavior even when tcache has free chunks.Source Code
Step-by-Step Walkthrough
1. Prepare Fake Chunk Structure
2. Setup Heap State
- Tcache bin (0xa0): 7 chunks (full)
- Unsorted bin: chunk0, chunk2
3. Convert to Smallbin
- Unsorted bin → Smallbin[0xa0]: chunk0, chunk2
4. Make Room in Tcache
- Tcache bin (0xa0): 5 chunks (has room for 2 more)
- Smallbin[0xa0]: chunk0 ← chunk2
5. Corrupt victim->bk (Vulnerability Point)
6. Trigger Stashing with calloc
calloc()bypasses tcache, goes to smallbin- Returns chunk0 to user
- Stashing begins: Move remaining smallbin chunks to tcache
- Process chunk2:
- Process fake chunk (stack_var):
Two key results:
- Our fake chunk is now in tcache
- A libc address was written to
stack_var[4](arbitrary write primitive!)
7. Get Arbitrary Pointer
Attack Summary Diagram
Prerequisites
1
Smallbin chunks
Ability to fill tcache and create chunks in smallbin
2
Overwrite bk pointer
Vulnerability allowing corruption of a smallbin chunk’s bk pointer
3
Writable target
A writable memory address to bypass the
bck->fd = bin check4
calloc() usage
Ability to call calloc() to trigger stashing (or another path that triggers smallbin→tcache stashing)
What You Gain
1. Arbitrary allocation:- Make malloc return a pointer to your fake chunk location
- Gain write access to stack, .bss, or other memory regions
- Write a libc address (main_arena pointer) to
fake_chunk->bk + 0x10 - Useful for:
- Defeating ASLR (if you can read back the written address)
- Overwriting function pointers with libc addresses
- Creating fake structures that need libc pointers
Related CTF Challenges
Hitcon 2019 - One Punch Man
Real-world CTF challenge using tcache stashing unlink attack
Try It Yourself
Practice on Ret2 Wargames
Debug this technique in an interactive browser environment
Detection and Mitigation
Why this works:- Tcache stashing code doesn’t fully validate chunk pointers
- The unlink-style operation trusts the bk pointer
- No validation that chunks being stashed are from the heap
- Validate bk pointers during stashing
- Check that chunks being moved to tcache are within heap bounds
- Add safe-linking style protections to bk pointers
Related Techniques
- [Unsorted Bin Attack/techniques/bins/unsorted-bin-attack) - Similar bk pointer corruption
- Tcache Poisoning - Direct tcache fd corruption
- [House of Lore/techniques/house/house-of-lore) - Smallbin corruption for arbitrary allocation
