Skip to main content

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 the bk pointer of a smallbin chunk, an attacker can:
  1. Make malloc return an arbitrary pointer
  2. 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 = bin check

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 + 0x10

Key Concepts

Tcache Stashing Mechanism

When tcache has space and you allocate from smallbin, glibc will:
  1. Remove chunks from smallbin
  2. Place them into tcache (“stashing”)
  3. Continue until tcache is full (7 chunks) or smallbin is empty
During this process, glibc performs an unlink-like operation:

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

Fake chunk layout:
The fake chunk’s bk pointer must point to a writable address because glibc will execute:
We set bk = &stack_var[2], so the write goes to stack_var[4].

2. Setup Heap State

State after frees:
  • Tcache bin (0xa0): 7 chunks (full)
  • Unsorted bin: chunk0, chunk2

3. Convert to Smallbin

This causes unsorted bin chunks to be sorted into their respective bins:
  • Unsorted bin → Smallbin[0xa0]: chunk0, chunk2

4. Make Room in Tcache

Current state:
  • Tcache bin (0xa0): 5 chunks (has room for 2 more)
  • Smallbin[0xa0]: chunk0 ← chunk2

5. Corrupt victim->bk (Vulnerability Point)

This is where the vulnerability is exploited. An attacker with a write primitive corrupts the bk pointer of a smallbin chunk.
Smallbin structure after corruption:

6. Trigger Stashing with calloc

What happens inside glibc:
  1. calloc() bypasses tcache, goes to smallbin
  2. Returns chunk0 to user
  3. Stashing begins: Move remaining smallbin chunks to tcache
  4. Process chunk2:
  5. Process fake chunk (stack_var):
Two key results:
  1. Our fake chunk is now in tcache
  2. A libc address was written to stack_var[4] (arbitrary write primitive!)

7. Get Arbitrary Pointer

Success! malloc returned a pointer to our controlled stack memory.

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 check
4

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
2. Arbitrary write of libc address:
  • 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

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
Potential mitigations:
  • Validate bk pointers during stashing
  • Check that chunks being moved to tcache are within heap bounds
  • Add safe-linking style protections to bk pointers
  • [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