回炉重修之house of storm 源码分析和调试
字数 2207 2025-08-22 22:47:39

House of Storm 攻击技术详解

1. 原理概述

House of Storm 是一种结合了 unsorted bin attack 和 Largebin attack 的高级堆利用技术,能够在特定条件下实现任意地址写。该技术主要针对 glibc 版本低于 2.30 的系统,因为 2.30 之后加入了安全检查机制。

2. 利用条件

要成功实施 House of Storm 攻击,需要满足以下条件:

  1. glibc 版本:必须小于 2.30(因为 2.30 之后加入了检查机制)
  2. 堆布局要求
    • 需要在 largebin 和 unsorted_bin 中分别布置一个 chunk
    • 这两个 chunk 在归位后必须处于同一个 largebin 的 index 中
    • unsortedbin 中的 chunk 要比 largebin 中的大
  3. 指针控制
    • 需要控制 unsorted_bin 中的 bk 指针
    • 需要控制 largebin 中的 bk 指针和 bk_nextsize 指针

3. 技术原理详解

3.1 核心思想

House of Storm 的核心在于同时利用两种攻击技术:

  1. unsorted bin attack:用于修改 unsorted_chunks(av)->bk 指针
  2. Largebin attack:用于修改目标地址的 size 字段

通过这两种攻击的结合,可以构造一个伪造的 chunk 并通过 malloc 返回,最终实现任意地址写。

3.2 关键代码分析

攻击主要发生在 _int_malloc 函数处理 unsorted bin 的过程中:

while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
    bck = victim->bk;
    size = chunksize(victim);
    // ... 省略检查代码 ...
    
    // 从 unsorted bin 中移除 victim
    unsorted_chunks(av)->bk = bck;
    bck->fd = unsorted_chunks(av);
    
    // 将 victim 放入对应的 bin 中
    if (in_smallbin_range(size)) {
        // 放入 small bin
    } else {
        // 放入 large bin
        victim_index = largebin_index(size);
        bck = bin_at(av, victim_index);
        fwd = bck->fd;
        
        if (fwd != bck) {
            size |= PREV_INUSE;
            if ((unsigned long)(size) < (unsigned long)(bck->bk->size)) {
                // 处理最小 size 的情况
                fwd = bck;
                bck = bck->bk;
                victim->fd_nextsize = fwd->fd;
                victim->bk_nextsize = fwd->fd->bk_nextsize;
                fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
            } else {
                // 查找合适的位置插入
                while ((unsigned long)size < fwd->size) {
                    fwd = fwd->fd_nextsize;
                }
                if ((unsigned long)size == (unsigned long)fwd->size) {
                    fwd = fwd->fd;
                } else {
                    // 插入到纵向链表中
                    victim->fd_nextsize = fwd;
                    victim->bk_nextsize = fwd->bk_nextsize;
                    fwd->bk_nextsize = victim;
                    victim->bk_nextsize->fd_nextsize = victim;
                }
                bck = fwd->bk;
            }
        } else {
            victim->fd_nextsize = victim->bk_nextsize = victim;
        }
    }
    
    // 将 victim 加入到 large bin 的链表中
    victim->bk = bck;
    victim->fd = fwd;
    fwd->bk = victim;
    bck->fd = victim;
}

3.3 攻击流程

  1. 布局堆内存

    • 在 unsorted bin 中布置一个较大的 chunk
    • 在 large bin 中布置一个稍小的 chunk
    • 确保两个 chunk 在同一个 large bin index 中
  2. 修改指针

    • 修改 unsorted bin chunk 的 bk 指针指向目标地址 (fake_chunk)
    • 修改 large bin chunk 的 bk 指针指向 fake_chunk+8
    • 修改 large bin chunk 的 bk_nextsize 指针指向 fake_chunk-0x18-5
  3. 触发攻击

    • 通过 malloc 触发 unsorted bin 处理流程
    • 同时触发 unsorted bin attack 和 large bin attack
  4. 伪造 chunk

    • unsorted bin attack 修改 unsorted_chunks(av)->bk 为 fake_chunk
    • large bin attack 在 fake_chunk+0x3 处写入堆地址 (0x55 或 0x56)
    • 这使得 fake_chunk 的 size 字段被设置为 0x55 或 0x56
  5. 绕过检查

    • 关键检查在 _libc_malloc 中:
      assert(!victim || chunk_is_mmapped(mem2chunk(victim)) || 
             ar_ptr == arena_for_chunk(mem2chunk(victim)));
      
    • 0x56 (01010110) 可以通过检查,因为 IS_MMAPPED 位被设置
    • 0x55 (01010101) 无法通过检查

4. 示例代码分析

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

struct {
    unsigned long presize;
    unsigned long size;
    unsigned long fd;
    unsigned long bk;
    unsigned long fd_nextsize;
    unsigned long bk_nextsize;
}chunk;

int main() {
    unsigned long *large_chunk, *unsorted_chunk;
    unsigned long *fake_chunk = (unsigned long *)&chunk;
    char *ptr;
    
    // 布局堆内存
    unsorted_chunk = malloc(0x418);
    malloc(0X20);
    large_chunk = malloc(0x408);
    malloc(0x20);
    
    // 释放 chunk 到 bins 中
    free(large_chunk);
    free(unsorted_chunk);
    unsorted_chunk = malloc(0x418);
    free(unsorted_chunk);
    
    // 修改指针准备攻击
    unsorted_chunk[1] = (unsigned long)fake_chunk;          // unsorted bin bk
    large_chunk[1] = (unsigned long)fake_chunk + 8;        // large bin bk
    large_chunk[3] = (unsigned long)fake_chunk - 0x18 - 5; // large bin bk_nextsize
    
    // 触发攻击
    ptr = malloc(0x48);
    
    // 利用
    strncpy(ptr, "/bin/sh\x00", 0x10);
    system(((char *)fake_chunk + 0x10));
    
    return 0;
}

5. 调试要点

  1. 堆布局阶段

    • 创建 unsorted_chunk (0x418) 和 large_chunk (0x408)
    • 通过释放和重新分配确保它们进入正确的 bins
  2. 指针修改阶段

    • unsorted_chunk[1] 修改 bk 指针
    • large_chunk[1] 修改 bk 指针
    • large_chunk[3] 修改 bk_nextsize 指针
  3. 攻击触发阶段

    • 观察 unsorted bin attack 如何修改 unsorted_chunks(av)->bk
    • 观察 large bin attack 如何修改 fake_chunk+0x3 处的值
  4. 伪造 chunk 检查

    • 确认 fake_chunk 的 size 字段被设置为 0x55 或 0x56
    • 只有 0x56 能通过后续的 assert 检查

6. 注意事项

  1. 分配大小选择

    • 通常使用 malloc(0x48) 触发攻击
    • 因为 fake_chunk 的 size 被视为 0x50 范围
    • 也可以使用 0x47 或 0x45,但会影响覆盖的完整性
  2. 字节对齐问题

    • malloc(0x45) 会导致覆盖少两字节
    • malloc(0x46) 会导致覆盖少一字节
    • malloc(0x48) 和 malloc(0x47) 都可以使用
  3. 利用目标

    • 通常用于覆盖 hook 函数(如 __malloc_hook)
    • 也可以用于构造 fake chunk 实现任意地址读写

7. 防御措施

  1. 升级 glibc:版本 2.30 及以上已经修复此问题
  2. 加强堆指针检查:验证 unsorted bin 和 large bin 中的指针有效性
  3. 启用安全机制:如 ASLR、FORTIFY_SOURCE 等

8. 参考资源

  1. 原始文章:回炉重修之house of storm 源码分析和调试
  2. glibc 源码分析
  3. 相关 CTF 题目解析

通过深入理解 House of Storm 攻击技术,可以更好地防御此类攻击,并在 CTF 比赛中有效利用此类技术解决难题。

House of Storm 攻击技术详解 1. 原理概述 House of Storm 是一种结合了 unsorted bin attack 和 Largebin attack 的高级堆利用技术,能够在特定条件下实现任意地址写。该技术主要针对 glibc 版本低于 2.30 的系统,因为 2.30 之后加入了安全检查机制。 2. 利用条件 要成功实施 House of Storm 攻击,需要满足以下条件: glibc 版本 :必须小于 2.30(因为 2.30 之后加入了检查机制) 堆布局要求 : 需要在 largebin 和 unsorted_ bin 中分别布置一个 chunk 这两个 chunk 在归位后必须处于同一个 largebin 的 index 中 unsortedbin 中的 chunk 要比 largebin 中的大 指针控制 : 需要控制 unsorted_ bin 中的 bk 指针 需要控制 largebin 中的 bk 指针和 bk_ nextsize 指针 3. 技术原理详解 3.1 核心思想 House of Storm 的核心在于同时利用两种攻击技术: unsorted bin attack :用于修改 unsorted_ chunks(av)->bk 指针 Largebin attack :用于修改目标地址的 size 字段 通过这两种攻击的结合,可以构造一个伪造的 chunk 并通过 malloc 返回,最终实现任意地址写。 3.2 关键代码分析 攻击主要发生在 _int_malloc 函数处理 unsorted bin 的过程中: 3.3 攻击流程 布局堆内存 : 在 unsorted bin 中布置一个较大的 chunk 在 large bin 中布置一个稍小的 chunk 确保两个 chunk 在同一个 large bin index 中 修改指针 : 修改 unsorted bin chunk 的 bk 指针指向目标地址 (fake_ chunk) 修改 large bin chunk 的 bk 指针指向 fake_ chunk+8 修改 large bin chunk 的 bk_ nextsize 指针指向 fake_ chunk-0x18-5 触发攻击 : 通过 malloc 触发 unsorted bin 处理流程 同时触发 unsorted bin attack 和 large bin attack 伪造 chunk : unsorted bin attack 修改 unsorted_ chunks(av)->bk 为 fake_ chunk large bin attack 在 fake_ chunk+0x3 处写入堆地址 (0x55 或 0x56) 这使得 fake_ chunk 的 size 字段被设置为 0x55 或 0x56 绕过检查 : 关键检查在 _libc_malloc 中: 0x56 (01010110) 可以通过检查,因为 IS_ MMAPPED 位被设置 0x55 (01010101) 无法通过检查 4. 示例代码分析 5. 调试要点 堆布局阶段 : 创建 unsorted_ chunk (0x418) 和 large_ chunk (0x408) 通过释放和重新分配确保它们进入正确的 bins 指针修改阶段 : unsorted_ chunk[ 1 ] 修改 bk 指针 large_ chunk[ 1 ] 修改 bk 指针 large_ chunk[ 3] 修改 bk_ nextsize 指针 攻击触发阶段 : 观察 unsorted bin attack 如何修改 unsorted_ chunks(av)->bk 观察 large bin attack 如何修改 fake_ chunk+0x3 处的值 伪造 chunk 检查 : 确认 fake_ chunk 的 size 字段被设置为 0x55 或 0x56 只有 0x56 能通过后续的 assert 检查 6. 注意事项 分配大小选择 : 通常使用 malloc(0x48) 触发攻击 因为 fake_ chunk 的 size 被视为 0x50 范围 也可以使用 0x47 或 0x45,但会影响覆盖的完整性 字节对齐问题 : malloc(0x45) 会导致覆盖少两字节 malloc(0x46) 会导致覆盖少一字节 malloc(0x48) 和 malloc(0x47) 都可以使用 利用目标 : 通常用于覆盖 hook 函数(如 __ malloc_ hook) 也可以用于构造 fake chunk 实现任意地址读写 7. 防御措施 升级 glibc :版本 2.30 及以上已经修复此问题 加强堆指针检查 :验证 unsorted bin 和 large bin 中的指针有效性 启用安全机制 :如 ASLR、FORTIFY_ SOURCE 等 8. 参考资源 原始文章: 回炉重修之house of storm 源码分析和调试 glibc 源码分析 相关 CTF 题目解析 通过深入理解 House of Storm 攻击技术,可以更好地防御此类攻击,并在 CTF 比赛中有效利用此类技术解决难题。