heap-exploitation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Heap Exploitation — Expert Attack Playbook

SKILL: 堆利用——专家攻击手册

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.
AI加载说明: 专家级glibc堆利用技术,涵盖ptmalloc2内部原理、bin结构、tcache机制、libc/堆地址泄露方法,以及根据glibc版本选择攻击方案。内容提炼自ctf-wiki堆章节、how2heap和真实场景利用经验。基础模型通常会混淆glibc版本限制,遗漏2.32版本引入的safe-linking(PROTECT_PTR)机制。

0. RELATED ROUTING

0. 相关跳转链接

  • 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
  • 栈溢出与ROP —— 适用于溢出发生在栈而非堆的场景
  • 格式化字符串漏洞利用 —— 通过格式化字符串泄露堆/libc地址
  • 任意写转RCE —— 将堆任意写转化为代码执行
  • 二进制保护绕过 —— 绕过ASLR/RELRO以有效利用堆写能力

Advanced References

进阶参考资料

  • HOUSE_OF_TECHNIQUES.md — House of Force/Spirit/Orange/Einherjar/Roman/Pig/Banana/Cat/Apple and tcache attacks
  • IO_FILE_EXPLOITATION.md — _IO_FILE vtable hijack, FSOP, stdout/stdin abuse, exit flow exploitation

  • HOUSE_OF_TECHNIQUES.md —— House of Force/Spirit/Orange/Einherjar/Roman/Pig/Banana/Cat/Apple等系列攻击及tcache攻击
  • IO_FILE_EXPLOITATION.md —— _IO_FILE 虚表劫持、FSOP、stdout/stdin滥用、exit流程利用

1. PTMALLOC2 STRUCTURE QUICK REFERENCE

1. PTMALLOC2 结构速查

malloc_chunk Layout (64-bit)

malloc_chunk 内存布局(64位)

         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)│
         └──────────────────────────┘
         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

Bin 类型

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
Bin类型大小范围(64位)结构出入栈规则
tcache (per-thread)≤ 0x410 (每个大小最多7个条目)单链表(next指针)LIFO
fastbin≤ 0x80 (默认)单链表(fd指针)LIFO
unsortedbin任意已释放大小双向循环链表FIFO
smallbin< 0x400双向循环链表FIFO
largebin≥ 0x400双向链表+按大小排序有序

Key Global Structures

核心全局结构

StructureLocationPurpose
main_arena
libc .data segmentContains bin heads, top chunk, system_mem
mp_
libc .datamalloc parameters (tcache settings, mmap threshold)
tcache_perthread_struct
Heap (first allocation)Per-thread tcache bins and counts

结构名存储位置用途
main_arena
libc .data段存储bin头部、top chunk、system_mem
mp_
libc .datamalloc参数(tcache配置、mmap阈值)
tcache_perthread_struct
堆(首次分配时创建)线程级tcache bin及计数

2. LEAK METHODS

2. 地址泄露方法

Libc Base Leak

Libc基地址泄露

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)
方法前置条件技术原理
Unsortedbin fd/bk释放一个大于tcache范围的chunk(或填满tcache)fd/bk指向
main_arena + 0x60
(不同版本可能是+0x70)→ 计算得到libc基地址
Smallbin fd/bkChunk从unsortedbin移动到smallbin和unsortedbin泄露原理一致
stdout FILE泄露
_IO_2_1_stdout_
写入数据
篡改
_IO_write_base
泄露libc数据(参考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
方法前置条件技术原理
Tcache fd指针释放两个tcache chunk,读取第一个的fd值fd指向堆地址(2.32及以上版本会做XOR混淆)
Fastbin fd释放两个fastbin chunkfd指向堆地址
UAF读对已释放chunk存在UAF读漏洞直接读取fd/bk值

Safe-Linking Decode (glibc ≥ 2.32)

Safe-Linking解码(glibc ≥ 2.32)

python
undefined
python
undefined

PROTECT_PTR: fd_stored = (chunk_addr >> 12) ^ real_fd

PROTECT_PTR: fd_stored = (chunk_addr >> 12) ^ real_fd

To decode: real_fd = fd_stored ^ (chunk_addr >> 12)

解码: real_fd = fd_stored ^ (chunk_addr >> 12)

To encode: fd_stored = (chunk_addr >> 12) ^ target_addr

编码: 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

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

3. 按glibc版本划分的攻击类别

glibc < 2.26 (No tcache)

glibc < 2.26 (无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
攻击方式所需原语效果
Fastbin dup双重释放任意地址分配
Unsortedbin attack篡改unsortedbin的bk指针
main_arena
地址写入目标位置(用于覆盖附近的__malloc_hook)
Unlink attack堆溢出覆盖prev_size + fd/bk任意写(需已知堆指针)
House of Force篡改top chunk大小任意地址分配
House of Spirit写入伪造的chunk头部在伪造chunk地址分配fastbin
Off-by-one null空字节溢出覆盖下一个chunk的size字段生成重叠chunk

glibc 2.26–2.28 (tcache, no key)

glibc 2.26–2.28 (有tcache,无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
攻击方式说明
Tcache poisoning篡改tcache fd → 任意地址分配,无大小校验
Tcache dup双重释放到tcache(此时还没有双重释放检测)
所有旧版本攻击仍然可用,但chunk会优先进入tcache

glibc 2.29–2.31 (tcache key introduced)

glibc 2.29–2.31 (引入tcache key校验)

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
攻击方式Tcache key绕过方法
Tcache dup第二次释放前篡改
key
字段(位于chunk+0x18位置)
House of Botcake双重释放:一个进unsortedbin,一个进tcache → 生成重叠chunk
Tcache stashing unlink利用smallbin→tcache refill逻辑获取任意chunk

glibc 2.32–2.33 (safe-linking / PROTECT_PTR)

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
攻击方式适配调整
Tcache poisoning使用
(chunk_addr >> 12) ^ target
编码目标地址
需要堆地址泄露需要堆地址来解码/编码safe-linking指针
Fastbin dup同样需要做编码处理

glibc ≥ 2.34 (hooks removed)

glibc ≥ 2.34 (移除hook)

ChangeImpact
__malloc_hook
removed
Cannot overwrite hook for one_gadget
__free_hook
removed
Cannot overwrite hook
__realloc_hook
removed
Cannot 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
.

变更影响
移除
__malloc_hook
无法覆盖hook来调用one_gadget
移除
__free_hook
无法覆盖该hook
移除
__realloc_hook
无法使用realloc技巧绕过one_gadget约束
2.34及以上版本目标: 参考任意写转RCE
_IO_FILE
exit_funcs
TLS_dtor_list
_dl_fini
相关利用方案。

4. COMMON VULNERABILITY PATTERNS

4. 常见漏洞模式

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
overflow
Clear PREV_INUSE → trigger backward consolidation
Uninitialized readRead heap memory without clearingLeak fd/bk from recycled chunk

漏洞类型描述利用路径
UAF (Use-After-Free)释放后仍可访问chunk读:泄露fd/bk;写:篡改fd实现tcache poisoning
Double Free对同一个chunk调用两次free()Tcache dup(绕过key)或fastbin dup
Heap Overflow写入超出chunk边界篡改下一个chunk的元数据(size、fd、bk)
Off-by-one单字节溢出空字节→缩小下一个chunk大小→生成重叠chunk
Off-by-null特指
\x00
溢出
清除PREV_INUSE标记→触发向后合并
Uninitialized read未清零就读取堆内存从回收的chunk中泄露fd/bk

5. TOOLS

5. 工具

bash
undefined
bash
undefined

pwndbg heap inspection

pwndbg堆检查命令

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
pwndbg> heap # 显示所有chunk pwndbg> bins # 显示所有bin内容 pwndbg> tcachebins # 查看tcache状态 pwndbg> fastbins # 查看fastbin状态 pwndbg> unsortedbin # 查看unsortedbin内容 pwndbg> vis_heap_chunks # 可视化堆布局 pwndbg> find_fake_fast &__malloc_hook # 查找附近的伪造fastbin chunk

how2heap — reference implementations

how2heap —— 参考实现

heapinspect

heapinspect

pip install heapinspect heapinspect <pid>
pip install heapinspect heapinspect <pid>

pwntools helpers

pwntools辅助代码

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

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

---

6. DECISION TREE

6. 决策树

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)
识别到堆漏洞
├── 可用原语是什么?
│   ├── UAF (读+写)
│   │   ├── 可以读取已释放chunk? → 泄露libc(unsortedbin)或堆地址(tcache fd)
│   │   └── 可以写入已释放chunk? → Tcache poisoning / fastbin dup
│   ├── 双重释放
│   │   ├── glibc < 2.29 → 直接使用tcache dup
│   │   ├── glibc 2.29-2.31 → 先篡改tcache key,或使用House of Botcake
│   │   └── glibc ≥ 2.32 → 需要堆地址泄露来做safe-linking编码
│   ├── 堆溢出(大小可控)
│   │   ├── 覆盖下一个chunk size → 重叠chunk → UAF
│   │   └── 直接覆盖fd → 任意地址分配
│   ├── Off-by-one / off-by-null
│   │   ├── 空字节写入size字段 → House of Einherjar(向后合并)
│   │   └── 单字节写入size字段 → 缩小chunk,生成重叠
│   └── 任意写(来自重叠或毒化分配)
│       ├── glibc < 2.34 → __malloc_hook / __free_hook → one_gadget
│       ├── glibc ≥ 2.34 → _IO_FILE vtable, exit_funcs, TLS_dtor_list
│       └── Partial RELRO → GOT覆盖
├── 需要泄露libc?
│   ├── 释放chunk到unsortedbin(大小>0x410或填满7个tcache条目)
│   ├── 读取fd/bk → 计算main_arena偏移 → 得到libc基地址
│   └── 替代方案:部分覆盖stdout FILE结构实现泄露
└── 需要泄露堆地址? (glibc ≥ 2.32)
    ├── 从已释放chunk读取tcache fd
    └── 解码: real_addr = stored_fd ^ (chunk_addr >> 12)