HTB Dream Diary Chapter 3:一道精彩的综合题
字数 2461 2025-09-01 11:26:03
HTB Dream Diary Chapter 3 堆利用综合技术详解
前言
本教学文档详细解析了一道综合性的堆利用题目,涵盖了多种高级利用技术。题目看似简单,实则融合了堆利用、格式化字符串泄露、栈溢出、ROP链构造和沙箱绕过等多种技术,是一道极具教学价值的综合题目。
题目概况
- 来源:HackTheBox Challenge
- 分类:pwn
- 难度:Hard
- 保护机制:全开(RELRO、Stack Canary、NX、PIE)
- 沙箱限制:黑名单系统调用包括fork、execve、vfork、open、creat
逆向分析
主函数结构
- 首先启用沙箱保护
- 提供经典菜单选项:
- 选项1:add - 分配内存
- 选项2:edit - 编辑内存(存在off-by-null漏洞)
- 选项3/4:free/print - 释放/打印内存
关键限制
- 分配大小限制在0x1f0以内,且不能是0xf8到0x10f之间
- 使用tcache机制(glibc 2.29)
- edit函数存在off-by-null漏洞
利用思路
总体计划
- 泄露堆地址和libc地址
- 利用house of einherjar技术制造堆块重叠
- 通过tcache dup修改__free_hook
- 泄露栈地址
- 构造ROP链调用mprotect
- 执行shellcode绕过沙箱
- 绕过execve限制读取flag
技术细节
1. 堆地址泄露
方法:
- 填满tcache bin使chunk进入unsorted bin
- 通过打印功能泄露堆地址
关键点:
- 需要构造合适的chunk布局
- 利用tcache bin链表特性泄露地址
2. house of einherjar技术
原理:
- 利用off-by-null漏洞修改chunk的size域的P位
- 伪造prev_size和size字段
- 绕过unlink检查实现堆块合并
实现步骤:
- 分配chunk A、B、C、D(D作为间隔)
- 释放A到unsorted bin
- 利用edit B的off-by-null修改C的P位
- 释放C触发合并,得到重叠块
关键检查绕过:
- 伪造prev_size与size匹配
- 伪造fd/bk指向自身(glibc 2.29新增检查)
3. libc地址泄露
方法:
- 通过重叠块访问unsorted bin中的main_arena指针
- 使用打印功能泄露libc地址
4. tcache dup技术
原理:
- 利用堆块重叠修改tcache的next指针
- 实现任意地址分配
实现步骤:
- 准备两个重叠块
- 修改tcache的next指针指向目标地址
- 分配chunk实现任意地址读写
5. 栈地址泄露
两种方法:
方法一:通过printf泄露
- 修改__free_hook为printf
- 构造格式化字符串泄露栈地址
方法二:通过environ泄露
- 获取environ变量地址(libc中)
- 读取environ值获取栈地址
6. ROP链构造
目标:
- 调用mprotect使shellcode可执行
- 跳转到shellcode
实现:
- 利用tcache dup覆盖返回地址
- 构造ROP链调用mprotect
- 设置shellcode地址并跳转
7. shellcode绕过沙箱
限制:
- execve等关键系统调用被禁用
- 需要替代方案读取文件
可用系统调用:
- execveat
- openat + sendfile(需知道文件名)
shellcode编写:
- 使用execveat(-100, "/bin/sh\x00")启动shell
- -100表示当前目录
8. 绕过execve读取flag
挑战:
- flag文件名难以猜测
- 无法直接使用open相关调用
解决方案:
- 使用shell内置命令:
echo *列出目录文件read line < flag; echo $line读取文件内容
详细利用步骤
阶段一:初始信息收集
- 填满tcache bin
- 创建特殊chunk布局
- 利用打印功能泄露堆地址
阶段二:house of einherjar利用
- 分配A(0x100)、B(0x80)、C(0x100)、D(0x20)
- 释放A到unsorted bin
- 编辑B触发off-by-null修改C的P位
- 释放C触发合并
- 得到重叠块控制B的内容
阶段三:libc地址泄露
- 从重叠块中读取main_arena指针
- 计算libc基址
阶段四:tcache dup修改__free_hook
- 准备两个重叠块
- 修改tcache的next指针指向__free_hook
- 分配chunk覆盖__free_hook为printf
阶段五:栈地址泄露
- 构造格式化字符串"%p.%p.%p.%p"
- 通过修改后的free触发printf
- 从输出中解析栈地址
阶段六:ROP链构造
- 利用tcache dup覆盖返回地址
- 构造ROP链:
- mprotect(heap_addr, size, PROT_EXEC)
- jmp shellcode
- 写入shellcode到堆中
阶段七:执行shellcode
- shellcode执行execveat启动shell
- 在shell中使用内置命令读取flag
关键代码片段
house of einherjar实现
# 分配初始chunk
add(0, 0x100, 'A'*0x100) # A
add(1, 0x80, 'B'*0x80) # B
add(2, 0x100, 'C'*0x100) # C
add(3, 0x20, 'D'*0x20) # D
# 释放A到unsorted bin
free(0)
# 编辑B触发off-by-null
edit(1, 'B'*0x80) # 注意末尾null字节会修改C的P位
# 释放C触发合并
free(2)
tcache dup修改__free_hook
# 假设已获得堆控制权
# 修改tcache的next指针
payload = p64(libc.sym['__free_hook'])
edit(overlapping_chunk, payload)
# 分配chunk覆盖__free_hook
add(4, 0x80, p64(libc.sym['printf']))
ROP链构造
# 构造ROP链
rop = ROP(libc)
rop.mprotect(heap_addr, 0x1000, 7) # RWX权限
rop.raw(heap_addr + shellcode_offset) # 跳转到shellcode
# 覆盖返回地址
payload = cyclic(offset) + rop.chain()
edit(stack_chunk, payload)
绕过沙箱的shellcode
; execveat(-100, "/bin/sh", 0, 0)
xor rdi, rdi
mov dil, -100
mov rsi, 0x68732f6e69622f ; "/bin/sh"
push rsi
mov rsi, rsp
xor rdx, rdx
xor r10, r10
mov rax, 322 ; execveat
syscall
总结
这道题目综合了多种高级利用技术,主要技术路线为:
- 利用off-by-null触发house of einherjar
- 通过堆块重叠实现tcache dup
- 修改__free_hook泄露栈地址
- 构造ROP链调用mprotect
- 执行手工编写的shellcode
- 使用shell内置命令绕过execve限制
每个技术环节都依赖前一个环节的成功,形成了完整的技术链条。这道题目对于理解现代堆利用技术和综合漏洞利用具有极高的教学价值。