Double-Free Heap Exploitation
A skill for understanding and exploiting double-free vulnerabilities in C programs.
What is a Double-Free?
A double-free occurs when the same memory block is freed more than once. This corrupts the heap allocator's internal data structures and can lead to:
- Memory corruption: Allocator metadata gets corrupted
- Overlapping pointers: Two pointers reference the same memory location
- Arbitrary writes: Attacker can control memory through one pointer affecting another
- Code execution: Can lead to ROP chains or shellcode execution
How It Works
The Mechanism
- First free: Memory block goes into a free list (fast bin or tcache)
- Second free: If another chunk is freed in between, the double-free check is bypassed
- Result: The same block appears twice in the free list
- Exploitation: When reallocating, you get overlapping pointers
Fast Bin Dup (Classic Double-Free)
// Fill tcache with 7 freed chunks
free(a); free(b); free(c); free(d); free(e); free(f); free(g);
// Double-free with intervening free
free(h); // First free of h
free(i); // Intervening free (bypasses double-free check)
free(h); // Second free of h - now h is in fast bin twice!
// Reallocate - i1 and i2 get the SAME address
char *i1 = malloc(10); // Gets h's address
char *i2 = malloc(10); // Also gets h's address (duplicate!)
Tcache Poisoning Variant
When tcache is involved, you can use null-byte overflows to corrupt size fields:
- Allocate chunks A, B, C of size 0x110
- Free B, then free A
- Reallocate A and overflow with null byte
- B's size becomes 0x100 instead of 0x111
- Free B again - now you have two tcache entries pointing to same address
Identifying Double-Free Vulnerabilities
Code Patterns to Look For
// Pattern 1: Direct double-free
void *ptr = malloc(100);
free(ptr);
// ... some code ...
free(ptr); // VULNERABLE
// Pattern 2: Conditional double-free
if (condition) {
free(ptr);
}
// ... code that might free ptr again ...
free(ptr); // VULNERABLE if condition was true
// Pattern 3: Function call double-free
void process(void *data) {
// ... uses data ...
free(data); // Frees it
}
// Caller also frees data - VULNERABLE
Debugging with GDB/Pwndbg
# Set breakpoints on free
break free
# Watch for suspicious patterns
watch *(void **)ptr
# Use pwndbg heap analysis
heap
heap chunks
vmmap
Exploitation Techniques
Technique 1: Fast Bin Dup for Address Disclosure
When you can't directly overwrite __malloc_hook:
- Fill tcache with 7 freed chunks of the same size
- Double-free a chunk (with intervening free)
- Reallocate to get overlapping pointers
- Write PIE address through one pointer
- Read through other pointer to leak address
- Calculate base address and proceed with ROP
Technique 2: Fast Bin Dup for Code Execution
- Target main_arena (contains PIE addresses, near
__malloc_hook) - Allocate chunk at specific offset of main_arena
- Continue allocating until reaching
__malloc_hook - Overwrite __malloc_hook with shellcode address
- Trigger malloc to execute shellcode
Technique 3: Tcache Poisoning
- Create double-free in tcache (not fast bin)
- Use overlapping pointers to corrupt tcache fd pointer
- Redirect malloc to arbitrary address
- Overwrite GOT entry or function pointer
Practical Example
Vulnerable Code Analysis
#include <stdio.h>
#include <stdlib.h>
int main() {
char *a = malloc(10);
char *b = malloc(10);
char *c = malloc(10);
free(a);
free(b);
free(a); // DOUBLE FREE - a is now in fast bin twice!
char *a1 = malloc(10); // Gets a's address
char *a2 = malloc(10); // Also gets a's address!
// Now a1 and a2 point to same memory
strcpy(a1, "controlled"); // Also modifies a2!
return 0;
}
Exploitation Steps
- Identify the double-free: Look for
free(ptr)called twice on same pointer - Verify with debugger: Check if addresses overlap after reallocation
- Plan the attack: Decide what you want to overwrite (GOT, function pointer, etc.)
- Craft the payload: Write through one pointer to affect the other
- Trigger the exploit: Call the function or trigger the code path
Common CTF Patterns
Dragon Army (HTB)
- Can only allocate fast-bin-sized chunks except 0x70
- Can't directly overwrite
__malloc_hook - Use PIE addresses (start with 0x56) as target
- Target
main_arenaoffset to reach__malloc_hook
Zero to Hero (PicoCTF)
- Uses tcache bins with null-byte overflow
- Corrupts size field from 0x111 to 0x100
- Creates double-free in tcache
- Leverages tcache poisoning for exploitation
Prevention and Mitigation
For Developers
// Always NULL out pointers after freeing
void *ptr = malloc(100);
free(ptr);
ptr = NULL; // Prevents double-free
// Use smart pointers in C++
std::unique_ptr<char[]> ptr(new char[100]);
// Automatically freed, can't double-free
For Security Testing
- Static analysis: Use tools like AddressSanitizer
- Dynamic analysis: Run with ASan to detect double-frees
- Code review: Look for all
free()calls and trace pointer ownership
References
Quick Reference
| Technique | When to Use | Key Requirement |
|---|---|---|
| Fast Bin Dup | Classic double-free | 7 freed chunks to fill tcache |
| Tcache Poisoning | Modern glibc | Tcache enabled, size field corruption |
| Address Disclosure | PIE binaries | Need to leak addresses first |
| Code Execution | Full control | Can overwrite function pointers |