House of storm学习
字数 3672
更新时间 2026-04-16 12:39:28

House of Storm 利用手法教学文档

一、背景与必要性

1.1 手法概述

House of Storm 是一种针对 glibc 堆管理器的组合利用手法,主要应用于 2.27 至 2.30 版本的 glibc。它通过精心构造的堆布局,结合 unsorted bin 和 large bin 的特定操作,实现在任意地址伪造 chunk 并成功申请出来,从而达成任意地址写的目的。

1.2 适用场景

  • 题目限制条件:只能通过 House of Storm 达成利用,其他常见手法(如 fastbin attack、tcache poisoning 等)均不可行
  • 关键限制:edit 次数有限(文中案例为 3 次),且每次写入大小受限(0x18 字节)
  • 内存分配:只能申请特定大小的堆块(文中为 0x440、0x430、0x48),其中 0x48 通过 calloc 分配
  • 信息泄露:show 功能只能使用一次,限制了信息泄露能力
  • 版本限制:glibc 2.30 之后由于增加了 smallbin 双向链表检查,该手法失效

二、前置知识要求

2.1 堆风水基础

  • 修改 size 位的 inuse 标志触发向前/向后合并
  • fastbin 的 fd 指针修改利用
  • tcache 相关利用:
    • 劫持 tcache_perthread_struct 结构体修改 count
    • 修改 tcache 的 fd 指针
    • 加密指针绕过技巧
    • key 检查绕过方法
  • unsorted bin attack:通过修改 bk 指针实现任意地址写
  • large bin attack:利用 glibc 的 large bin 管理算法修改 bk_nextsize

2.2 其他 House 手法了解

  • House of Botcake:利用 unsorted bin 实现 double free
  • House of Apple:伪造 FILE 结构体的 wide_data 和 vtable
  • House of Einherjar:利用 prev_inuse 位伪造前一个 chunk
  • House of Orange:改小 top chunk 触发 sysmalloc
  • House of Spirit:伪造 size 字段使 fake chunk 被链入
  • House of Force:修改 top chunk size
  • House of Wiki:通过 malloc_assert 触发 IO
  • House of Emma:爆破修改成员偏移
  • House of Water:在 tcache 上残留 libc 地址
  • House of Pig:劫持 tcache_perthread_struct 结构体打 IO
  • House of Banana:劫持动态链接器打 IO

三、漏洞环境分析

3.1 程序功能分析

基于文档中的 2026 CISCN 半决赛题目 "catchme",程序提供以下功能:

  1. add (adopt_creature)

    • 可申请三种大小的堆块:
      • 0x430 字节(通过 malloc)
      • 0x440 字节(通过 malloc)
      • 0x48 字节(通过 calloc)
    • 最多同时存在 5 个堆块指针
    • calloc 在 2.41 之前版本不会使用 tcache
  2. delete (release_creature)

    • 存在 Use-After-Free (UAF) 漏洞
    • 释放后不立即清空指针,直到调用 purge_record
  3. show (inspect_creature_tag_once)

    • 一次性读取功能,整个进程只能使用一次
    • 读取堆块偏移 +0x8 处的 tag 字段
  4. edit (engrave_creature_tag)

    • 最多可使用 3 次
    • 每次最多写入 0x18 字节
    • 写入位置为堆块偏移 +0x8 处的 tag 字段
  5. purge_record

    • 仅清空指针,不释放内存
    • 用于清理悬垂指针记录

3.2 限制条件总结

  • 信息泄露极度有限:仅一次 show 机会
  • 写入能力受限:最多 3 次 edit,每次 0x18 字节
  • 分配大小固定:只有三种特定大小的堆块
  • 无 IO 相关函数:难以通过常规 IO 流攻击
  • 无法通过 fastbin/tcache 直接攻击:大小不合适且 calloc 不使用 tcache

四、House of Storm 核心机制

4.1 利用原理

House of Storm 的核心在于同时利用 unsorted bin 和 large bin 的链表操作,在任意地址伪造一个 chunk 并将其链入 unsorted bin,从而可以从该地址申请出 chunk。

4.2 关键源码分析

4.2.1 unsorted bin 遍历过程

当 malloc 从 unsorted bin 中取出 chunk 时,会执行以下操作:

while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
    bck = victim->bk;
    // ... 检查和其他处理
}

4.2.2 关键写入操作

在 unsorted bin 处理过程中,有以下关键写入:

  1. unsorted bin 链入

    bck->fd = unsorted_chunks(av);  // 写入位置:victim->bk + 0x10
    
  2. large bin 链入

    • 当 unsorted chunk 需要放入 large bin 时:
    // 如果 unsorted_chunk->size > largebin_chunk->size
    unsorted_chunk->fd_nextsize = largebin_chunk;
    unsorted_chunk->bk_nextsize = largebin_chunk->bk_nextsize;
    largebin_chunk->bk_nextsize = unsorted_chunk;
    unsorted_chunk->bk_nextsize->fd_nextsize = unsorted_chunk;  // 关键写入
    
    • 后续链表链接:
    bck = largebin_chunk->bk;
    unsorted_chunk->bk = bck;
    unsorted_chunk->fd = largebin_chunk;
    largebin_chunk->bk = unsorted_chunk;
    bck->fd = unsorted_chunk;  // 关键写入
    

4.3 伪造 chunk 的构造

要成功利用 House of Storm,需要构造以下条件:

  1. 控制两个 chunk

    • 一个 unsorted bin chunk(称为 chunk A)
    • 一个 large bin chunk(称为 chunk B)
  2. 关键字段设置

    假设目标地址为 fake_chunk
    
    对于 chunk A(unsorted bin):
    chunk_A->bk = fake_chunk
    
    对于 chunk B(large bin):
    chunk_B->bk = fake_chunk + 0x8
    chunk_B->bk_nextsize = fake_chunk - 0x18 - 5
    
  3. 写入结果

    • 第一次写入:*(fake_chunk + 0x10) = main_arena + 0x60(unsorted bin 链表头)
    • 第二次写入:*(fake_chunk + 0x3) = chunk_A(通过 bk_nextsize->fd_nextsize)
    • 第三次写入:*(fake_chunk + 0x18) = chunk_A(通过 bck->fd)

4.4 版本限制原因

在 glibc 2.30 之后,增加了 smallbin 双向链表检查:

bck = victim->bk;
if (__glibc_unlikely(bck->fd != victim)) {
    malloc_printerr("malloc(): smallbin double linked list corrupted");
}

这个检查使得 House of Storm 无法在 2.30 及之后版本中使用。

五、利用步骤详解

5.1 准备工作

  1. 堆布局

    • 创建两个 large bin 大小的 chunk(0x440)
    • 确保它们进入 large bin
    • 创建 unsorted bin chunk
  2. 信息泄露

    • 通过唯一的 show 机会泄露 libc 地址
    • 可能需要泄露堆地址(如果题目需要)

5.2 核心利用步骤

  1. 释放 chunk 到 large bin

    # 假设 chunk0 和 chunk2 是 large bin 大小的 chunk
    delete(0)  # chunk0 进入 unsorted bin
    delete(2)  # chunk2 进入 unsorted bin
    
    # 触发合并或重新分配,使它们进入 large bin
    add(0x440)  # 重新分配,触发整理
    
  2. 构造伪造指针

    # 计算目标地址,通常是 __free_hook - 0x18
    fake_chunk = libc_base + libc.sym["__free_hook"] - 0x18
    
    # 第一次编辑:修改 unsorted bin chunk 的 bk
    edit(unsorted_chunk_idx, p64(fake_chunk))
    
    # 第二次编辑:修改 large bin chunk 的 bk 和 bk_nextsize
    edit(largebin_chunk_idx, 
         p64(fake_chunk + 8) +  # bk
         p64(0) +               # fd_nextsize
         p64(fake_chunk - 0x18 - 5))  # bk_nextsize
    
  3. 触发分配

    • 申请一个合适大小的 chunk
    • 由于伪造的 chunk 已经被链入 unsorted bin,这次分配会从 fake_chunk 处"切割"内存
    • 成功获得目标地址附近的可控内存

5.3 后续利用

  1. 写 __free_hook

    • 由于 fake_chunk 在 __free_hook - 0x18 处
    • 通过编辑可以修改 __free_hook 为 system 或其他目标函数
  2. 触发 shell

    • 释放一个包含 "/bin/sh" 字符串的 chunk
    • 触发 __free_hook 执行 system("/bin/sh")

六、防御与检测

6.1 缓解措施

  1. 更新 glibc:升级到 2.30 或更高版本
  2. 安全编译选项
    • 开启 Full RELRO
    • 开启 PIE
    • 使用堆栈保护
  3. 代码审计:检查 UAF 漏洞和适当的指针清理

6.2 检测方法

  1. 动态检测
    • 监控异常的 bk/bk_nextsize 指针值
    • 检测 unsorted bin 和 large bin 链表异常
  2. 静态分析
    • 检查堆操作中的指针使用
    • 识别未初始化的指针访问

七、总结与延伸

7.1 手法特点

  • 组合利用:需要同时控制 unsorted bin 和 large bin
  • 条件苛刻:需要精确的堆布局和有限次数的编辑操作
  • 历史手法:仅适用于特定版本的 glibc(2.27-2.29)

7.2 学习价值

  1. 深入理解堆管理:通过此手法可以深入理解 glibc 的 bin 管理机制
  2. 组合利用思维:学习如何将多个漏洞或条件组合成完整利用链
  3. 版本适应性:理解不同 glibc 版本的安全机制差异

7.3 相关资源

  1. 替代手法:在更新版本中,可研究 House of Emma、House of Pig 等新手法
  2. 调试技巧:使用 gdb 插件(如 pwndbg、gef)观察 bin 状态变化
  3. 练习平台:尝试在 glibc 2.27-2.29 环境中复现此手法

注:本文档基于提供的链接内容编写,详细描述了 House of Storm 利用手法的原理、步骤和实现细节。在实际应用中,请确保在合法授权的环境中进行测试和学习。

相似文章
相似文章
 全屏