Heap Exploitation
Heap exploitation techniques covering heap memory management, use-after-free, double-free, heap overflow, and tcache poisoning attacks.
Contents
- Heap Basics
- Memory Layout
- Chunk Structure (malloc_chunk)
- Size Field Flags (3 LSBs)
- Allocated vs Free Chunk
- Bins (Free Chunk Storage)
- Tcache Bins (glibc >= 2.26)
- Fast Bins
- Unsorted Bin
- Small Bins
- Large Bins
- Common Heap Attacks
- Use-After-Free (UAF)
- Double Free
- Heap Overflow
- Fastbin Dup / Tcache Poisoning
- Unlink Attack
- House Techniques
- Useful GDB Commands (with pwndbg/GEF)
- Key glibc Version Changes
- References
Heap Exploitation
Sources: HackTricks - Libc Heap | How2Heap
Heap Basics
The heap is dynamic memory allocated at runtime via malloc(), calloc(), realloc() and freed with free().
char *ptr = malloc(0x20); // Allocate 0x20 bytes on heap
strcpy(ptr, "hello");
free(ptr); // Return chunk to allocator
Memory Layout
[ Text | Data | BSS | Heap → ... ← Stack ]
↑ grows up ↑ grows down
Chunk Structure (malloc_chunk)
struct malloc_chunk {
size_t prev_size; // Size of previous chunk (if free)
size_t size; // Size of this chunk + flags (3 LSBs)
// --- user data starts here when allocated ---
chunk* fd; // Forward pointer (only when free)
chunk* bk; // Backward pointer (only when free)
chunk* fd_nextsize; // Large bins only
chunk* bk_nextsize; // Large bins only
};
Size Field Flags (3 LSBs)
| Bit | Name | Meaning |
|---|---|---|
| 0x1 | PREV_INUSE | Previous chunk is allocated |
| 0x2 | IS_MMAPPED | Chunk obtained via mmap() |
| 0x4 | NON_MAIN_ARENA | Chunk from secondary arena |
Allocated vs Free Chunk
Allocated:
+------------------+
| prev_size | (or prev chunk's data)
+------------------+
| size | A|M|P|
+------------------+
| user data |
| ... |
+------------------+
Free:
+------------------+
| prev_size |
+------------------+
| size | A|M|P|
+------------------+
| fd | → next free chunk
+------------------+
| bk | → prev free chunk
+------------------+
| (unused) |
+------------------+
Bins (Free Chunk Storage)
When chunks are freed, they’re placed in bins for reuse:
Tcache Bins (glibc >= 2.26)
- Per-thread cache, fastest allocation
- 64 singly-linked lists (sizes 0x20 to 0x410 on x64)
- Max 7 chunks per bin
- LIFO (Last In, First Out)
- Minimal security checks (exploit-friendly)
Fast Bins
- Sizes: 0x20 to 0x80 (x64)
- Singly-linked (LIFO), never coalesced
- Chunks NOT marked as free (
PREV_INUSEstays set) - Filled after tcache is full
Unsorted Bin
- Single doubly-linked list
- “Staging area” — freed chunks land here first (if not tcache/fastbin)
- On next
malloc(), chunks sorted into small/large bins or reused
Small Bins
- 62 doubly-linked lists
- Sizes: 0x20 to 0x3F0 (x64)
- FIFO (First In, First Out)
- Exact size match
Large Bins
- 63 doubly-linked lists
- Sizes > 0x3F0 (x64)
- Sorted by size within each bin
- Best-fit allocation
Common Heap Attacks
Use-After-Free (UAF)
Accessing memory after free(). If chunk is reallocated, attacker controls data.
char *a = malloc(0x20);
free(a); // 'a' is freed but pointer still exists
char *b = malloc(0x20); // b gets same chunk as 'a'
// Writing to 'a' now overwrites 'b's data
Double Free
Freeing same chunk twice → appears in free list twice → two allocations return same chunk.
free(a);
free(b); // or something else to avoid glibc double-free check
free(a); // 'a' in free list twice
// Now malloc() returns 'a' twice → overlapping chunks
Tcache Double Free (glibc < 2.29): No key check, trivial.
Tcache Double Free (glibc >= 2.29): Tcache adds a key field; must overwrite it.
Heap Overflow
Overflowing from one chunk into adjacent chunk’s metadata.
Fastbin Dup / Tcache Poisoning
Corrupt fd pointer of free chunk → next allocation at arbitrary address.
# Tcache poisoning (simplified)
# 1. Free chunk A → tcache: A
# 2. Overwrite A->fd to point to target
# 3. malloc() → returns A
# 4. malloc() → returns TARGET (arbitrary write!)
Unlink Attack
Corrupt fd/bk of a chunk to get arbitrary write during unlink().
Modern glibc has safety checks: P->fd->bk == P && P->bk->fd == P
House Techniques
| Technique | Target | Condition |
|---|---|---|
| House of Force | Top chunk | Overflow into top chunk size |
| House of Spirit | Fastbin/Tcache | Forge fake chunk, free it |
| House of Lore | Small bin | Corrupt bk pointer |
| House of Einherjar | Backward consolidation | Off-by-one null byte |
| House of Orange | _IO_FILE / unsorted bin | No free() needed |
| House of Roman | Partial overwrite | No leak needed |
| House of Rabbit | Fastbin consolidation | Corrupt fastbin fd |
Useful GDB Commands (with pwndbg/GEF)
# Inspect heap state
heap # Overview of all chunks
bins # Show all bins (tcache, fast, unsorted, small, large)
vis_heap_chunks # Visual representation
tcachebins # Tcache specifically
fastbins
smallbins
# Inspect specific chunk
heap chunk <addr>
malloc_chunk <addr>
# Track allocations
# set breakpoints on malloc/free
b *malloc
b *free
Key glibc Version Changes
| Version | Change |
|---|---|
| 2.26 | Tcache introduced |
| 2.29 | Tcache key (double-free check) |
| 2.32 | Tcache fd pointer PROTECT_PTR (XOR with heap addr >> 12) |
| 2.34 | __malloc_hook / __free_hook removed |
| 2.35+ | Further hardening of pointer mangling |