PluginBench
Skill
Fail
Audit score 45

heap-exploitation

yaklang/hack-skills

Expert glibc heap exploitation: ptmalloc2 internals, tcache/fastbin attacks, and version-specific techniques.

What is heap-exploitation?

Comprehensive playbook for exploiting heap vulnerabilities in glibc (ptmalloc2), including UAF, double free, overflow, and off-by-one attacks. Use when targeting heap-based memory corruption to achieve arbitrary write or code execution, with specific guidance for glibc versions 2.26–2.34 and safe-linking bypass.

  • Covers ptmalloc2 chunk layout, bin structures (tcache, fastbin, unsortedbin, smallbin, largebin), and malloc internals
  • Provides libc and heap leak methods including unsortedbin fd/bk extraction and safe-linking decoding (glibc ≥2.32)
  • Details version-specific attacks: fastbin dup, tcache poisoning, House of techniques, unlink, and off-by-one/null exploits
  • Explains glibc version constraints and protections (tcache key in 2.29+, PROTECT_PTR in 2.32+, hook removal in 2.34+)
  • Includes decision tree for selecting attacks based on vulnerability primitive (UAF, double free, overflow)
  • Integrates with pwndbg, how2heap, and pwntools for practical exploitation

How to install heap-exploitation

npx skills add https://github.com/yaklang/hack-skills --skill heap-exploitation
Prerequisites
  • Understanding of C memory layout and malloc/free semantics
  • Familiarity with glibc internals or access to ctf-wiki and how2heap references
  • Debugger setup (pwndbg recommended) for heap inspection and verification
  • Knowledge of the target binary's glibc version and enabled protections (ASLR, RELRO, stack canaries)
Claude Code
Cursor
Windsurf
Cline

How to use heap-exploitation

  1. 1.Identify the heap vulnerability type (UAF, double free, overflow, off-by-one) and confirm the primitive (read/write capability)
  2. 2.Determine the target glibc version and applicable protections (safe-linking, tcache key, hook availability)
  3. 3.Select the appropriate attack from the decision tree: leak methods first (unsortedbin/tcache fd), then exploitation (tcache poisoning/fastbin dup)
  4. 4.Implement the attack using pwntools, encoding pointers correctly for glibc ≥2.32 (safe-linking XOR with chunk_addr >> 12)
  5. 5.Use pwndbg commands (heap, bins, tcachebins, vis_heap_chunks) to verify chunk state and validate exploitation progress
  6. 6.Chain the heap write primitive into code execution via arbitrary-write-to-rce skill (hook overwrite for <2.34, _IO_FILE/_dl_fini for ≥2.34)

Use cases

Good for
  • Exploit UAF in a service to leak libc base via unsortedbin fd pointer, then tcache poison for arbitrary write
  • Bypass tcache key protection (glibc 2.29–2.31) by corrupting the key field before double-free attack
  • Decode safe-linked pointers (glibc ≥2.32) using heap leak to perform tcache poisoning with correct XOR encoding
  • Chain heap overflow into next chunk metadata corruption to create overlapping chunks for information disclosure
  • Leverage off-by-one null byte to clear PREV_INUSE flag and trigger backward consolidation for chunk overlap
Who it's for
  • Binary exploitation researchers and CTF competitors targeting heap vulnerabilities
  • Security engineers performing memory safety audits on C/C++ applications using glibc
  • Penetration testers exploiting heap-based memory corruption in services
  • Vulnerability researchers analyzing ptmalloc2 behavior across glibc versions

heap-exploitation FAQ

What is safe-linking and how do I bypass it in glibc ≥2.32?

Safe-linking (PROTECT_PTR) XORs heap pointers with (chunk_addr >> 12) to prevent pointer disclosure. To encode a target: fd_stored = (chunk_addr >> 12) ^ target_addr. To decode: real_fd = fd_stored ^ (chunk_addr >> 12). You must leak the heap base address first.

How do I exploit tcache in glibc 2.29–2.31 when tcache key is present?

Corrupt the tcache key field (at chunk+0x18) before freeing the second copy, or use House of Botcake to place one chunk in unsortedbin and one in tcache for overlapping access without triggering double-free detection.

Can I still use __malloc_hook and __free_hook in glibc ≥2.34?

No, hooks were removed in glibc 2.34. Use arbitrary-write-to-rce skill instead: target _IO_FILE vtable hijack, exit_funcs, TLS_dtor_list, or _dl_fini for code execution.

What is the difference between fastbin and tcache attacks?

Fastbin (≤0x80) is global and LIFO; tcache (≤0x410, 7 per size) is per-thread and LIFO. Tcache is checked first on malloc, so tcache poisoning is often simpler. Fastbin has size checks; tcache (pre-2.29) does not.

How do I leak libc base from a heap vulnerability?

Free a chunk larger than tcache range (or fill tcache first) to move it to unsortedbin. Read the fd or bk pointer: it points to main_arena + 0x60 (or +0x70 depending on version). Subtract the offset to get libc base.

Full instructions (SKILL.md)

Source of truth, from yaklang/hack-skills.


name: heap-exploitation description: >- Heap exploitation playbook. Use when targeting ptmalloc2/glibc heap vulnerabilities including UAF, double free, overflow, off-by-one/null, and leveraging tcache/fastbin/unsortedbin attacks for arbitrary write or code execution.

SKILL: Heap Exploitation — Expert Attack Playbook

AI LOAD INSTRUCTION: Expert glibc heap exploitation techniques. Covers ptmalloc2 internals, bin structures, tcache mechanics, libc/heap leak methods, and attack selection by glibc version. Distilled from ctf-wiki heap sections, how2heap, and real-world exploitation. Base models often confuse glibc version constraints and miss safe-linking (PROTECT_PTR) introduced in 2.32.

0. RELATED ROUTING

  • stack-overflow-and-rop — when the overflow is on the stack rather than the heap
  • format-string-exploitation — leak heap/libc addresses via format string
  • arbitrary-write-to-rce — convert heap arbitrary write into code execution
  • binary-protection-bypass — bypass ASLR/RELRO to use heap write effectively

Advanced References


1. PTMALLOC2 STRUCTURE QUICK REFERENCE

malloc_chunk Layout (64-bit)

         chunk pointer (returned by malloc - 0x10)
         ┌──────────────────────────┐
    0x00 │  prev_size (if prev free)│
    0x08 │  size        | A | M | P │  ← P=PREV_INUSE, M=IS_MMAPPED, A=NON_MAIN_ARENA
         ├──────────────────────────┤  ← user data starts here (returned pointer)
    0x10 │  fd (if free)            │  ← forward pointer to next free chunk
    0x18 │  bk (if free)            │  ← backward pointer to prev free chunk
    0x20 │  fd_nextsize (large only)│
    0x28 │  bk_nextsize (large only)│
         └──────────────────────────┘

Bin Types

BinSize Range (64-bit)StructureLIFO/FIFO
tcache (per-thread)≤ 0x410 (7 entries per size)Singly linked (next pointer)LIFO
fastbin≤ 0x80 (default)Singly linked (fd)LIFO
unsortedbinAny freed sizeDoubly linked circularFIFO
smallbin< 0x400Doubly linked circularFIFO
largebin≥ 0x400Doubly linked + size-sortedSorted

Key Global Structures

StructureLocationPurpose
main_arenalibc .data segmentContains bin heads, top chunk, system_mem
mp_libc .datamalloc parameters (tcache settings, mmap threshold)
tcache_perthread_structHeap (first allocation)Per-thread tcache bins and counts

2. LEAK METHODS

Libc Base Leak

MethodPreconditionTechnique
Unsortedbin fd/bkFree a chunk > tcache range (or fill tcache)fd/bk → main_arena + 0x60 (or +0x70 depending on version) → libc base
Smallbin fd/bkChunk moved from unsortedbin to smallbinSame as unsortedbin leak
stdout FILE leakWrite to _IO_2_1_stdout_Corrupt _IO_write_base to leak libc data (see IO_FILE)

Heap Base Leak

MethodPreconditionTechnique
Tcache fd pointerFree two tcache chunks, read first's fdfd → heap address (XOR'd in ≥ 2.32)
Fastbin fdFree two fastbin chunksfd → heap address
UAF readUse-after-free on freed chunkRead fd/bk directly

Safe-Linking Decode (glibc ≥ 2.32)

# PROTECT_PTR: fd_stored = (chunk_addr >> 12) ^ real_fd
# To decode: real_fd = fd_stored ^ (chunk_addr >> 12)
# To encode: fd_stored = (chunk_addr >> 12) ^ target_addr

def deobfuscate(stored_fd, chunk_addr):
    return stored_fd ^ (chunk_addr >> 12)

def obfuscate(target, chunk_addr):
    return (chunk_addr >> 12) ^ target

3. ATTACK CATEGORIES BY GLIBC VERSION

glibc < 2.26 (No tcache)

AttackPrimitive NeededResult
Fastbin dupDouble freeArbitrary allocation
Unsortedbin attackCorrupt unsortedbin bkWrite main_arena addr to target (used for __malloc_hook nearby overwrite)
Unlink attackHeap overflow into prev_size + fd/bkArbitrary write (with known heap pointer)
House of ForceTop chunk size overwriteArbitrary allocation
House of SpiritWrite fake chunk headerFastbin allocation at fake chunk
Off-by-one nullNull byte overflow into next chunk sizeOverlapping chunks

glibc 2.26–2.28 (tcache, no key)

AttackNotes
Tcache poisoningOverwrite tcache fd → arbitrary allocation, no size check
Tcache dupDouble free into tcache (no double-free detection yet)
All previous attacksStill work, but chunks go to tcache first

glibc 2.29–2.31 (tcache key introduced)

AttackBypass for tcache key
Tcache dupCorrupt key field (at chunk+0x18) before second free
House of BotcakeDouble free: one in unsortedbin, one in tcache → overlapping
Tcache stashing unlinkAbuse smallbin→tcache refill to get arbitrary chunk

glibc 2.32–2.33 (safe-linking / PROTECT_PTR)

AttackAdaptation
Tcache poisoningEncode target with (chunk_addr >> 12) ^ target
Heap leak requiredNeed heap addr to decode/encode safe-linked pointers
Fastbin dupSame encoding required

glibc ≥ 2.34 (hooks removed)

ChangeImpact
__malloc_hook removedCannot overwrite hook for one_gadget
__free_hook removedCannot overwrite hook
__realloc_hook removedCannot use realloc trick for one_gadget constraints

Post-2.34 targets: see arbitrary-write-to-rce for _IO_FILE, exit_funcs, TLS_dtor_list, _dl_fini.


4. COMMON VULNERABILITY PATTERNS

VulnerabilityDescriptionExploitation Path
UAF (Use-After-Free)Access chunk after freeRead: leak fd/bk; Write: corrupt fd for tcache poisoning
Double Freefree() same chunk twiceTcache dup (bypass key) or fastbin dup
Heap OverflowWrite past chunk boundaryCorrupt next chunk's metadata (size, fd, bk)
Off-by-oneOne byte overflowNull byte → shrink next chunk size → overlapping chunks
Off-by-nullSpecifically \x00 overflowClear PREV_INUSE → trigger backward consolidation
Uninitialized readRead heap memory without clearingLeak fd/bk from recycled chunk

5. TOOLS

# pwndbg heap inspection
pwndbg> heap                      # display all chunks
pwndbg> bins                      # show all bin contents
pwndbg> tcachebins                # tcache status
pwndbg> fastbins                  # fastbin status
pwndbg> unsortedbin               # unsortedbin content
pwndbg> vis_heap_chunks           # visual heap layout
pwndbg> find_fake_fast &__malloc_hook  # find nearby fake fastbin chunks

# how2heap — reference implementations
git clone https://github.com/shellphish/how2heap

# heapinspect
pip install heapinspect
heapinspect <pid>

# pwntools helpers
from pwn import *
libc = ELF('./libc.so.6')
print(hex(libc.symbols['__malloc_hook']))
print(hex(libc.symbols['__free_hook']))

6. DECISION TREE

Heap vulnerability identified
├── What is the primitive?
│   ├── UAF (read + write)
│   │   ├── Can read freed chunk? → Leak libc (unsortedbin) or heap (tcache fd)
│   │   └── Can write freed chunk? → Tcache poisoning / fastbin dup
│   ├── Double free
│   │   ├── glibc < 2.29 → direct tcache dup
│   │   ├── glibc 2.29-2.31 → corrupt tcache key first, or House of Botcake
│   │   └── glibc ≥ 2.32 → need heap leak for safe-linking encode
│   ├── Heap overflow (controlled size)
│   │   ├── Overwrite next chunk size → overlapping chunks → UAF
│   │   └── Overwrite fd directly → arbitrary allocation
│   ├── Off-by-one / off-by-null
│   │   ├── Null byte into size → House of Einherjar (backward consolidation)
│   │   └── One byte into size → shrink chunk, create overlap
│   └── Arbitrary write (from overlap or poisoned allocation)
│       ├── glibc < 2.34 → __malloc_hook / __free_hook → one_gadget
│       ├── glibc ≥ 2.34 → _IO_FILE vtable, exit_funcs, TLS_dtor_list
│       └── Partial RELRO → GOT overwrite
│
├── Need libc leak?
│   ├── Free chunk into unsortedbin (size > 0x410 or fill 7 tcache)
│   ├── Read fd/bk → main_arena offset → libc base
│   └── Alternative: stdout FILE partial overwrite for leak
│
└── Need heap leak? (glibc ≥ 2.32)
    ├── Read tcache fd from freed chunk
    └── Decode: real_addr = stored_fd ^ (chunk_addr >> 12)