[原创]FastBin Attack:House of spirit attack
2023-5-6 16:32:8 Author: bbs.pediy.com(查看原文) 阅读量:14 收藏

该技术主要是将一块可控的内存精心构造(fake chunk),以欺骗free通过其检查,令堆管理器将我们构造的内存块视作堆的chunk,进入bins中。
house of spirit attack常常需要搭配其他攻击手段,也常常是攻击链条中的一环。我们以2014 hack.lu oreo作为例子展示一种house of spirit的攻击场景。
在此之前我们先通过how2heap的例子来说明应该如何精心构造内存才能通过free检查进入bins。

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

int main()

{

    setbuf(stdout, NULL);

    puts("This file demonstrates the house of spirit attack.");

    puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.");

    puts("Required primitives: known target address, ability to set up the start/end of the target memory");

    puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache");

    void *chunks[7];

    for(int i=0; i<7; i++) {

        chunks[i] = malloc(0x30);

    }

    for(int i=0; i<7; i++) {

        free(chunks[i]);

    }

    puts("\nStep 2: Prepare the fake chunk");

    // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)

    long fake_chunks[10] __attribute__ ((aligned (0x10)));

    printf("The target fake chunk is at %p\n", fake_chunks);

    printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]);

    printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");

    puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end.");

    printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]);

    fake_chunks[1] = 0x40; // this is the size

    printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");

    printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]);

    fake_chunks[9] = 0x1234; // nextsize

    puts("\nStep 3: Free the first fake chunk");

    puts("Note that the address of the fake chunk must be 16-byte aligned.\n");

    void *victim = &fake_chunks[2];

    free(victim);

    puts("\nStep 4: Take out the fake chunk");

    printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]);

    printf("malloc can do the trick as well, you just need to do it for 8 times.");

    void *allocated = calloc(1, 0x30);

    printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim);

    assert(allocated == victim);

}

这个例子展示了如何绕过free的检查使得我们构造的fake chunk能够进入bins,大部分都是构造一个fast chunk,这里也不例外。需要注意的是long fake_chunks[10] __attribute__ ((aligned (0x10)));写法是告诉编译器给我的内存地址要是0x10对齐的。

下图展示了chunk块free后想要进入fastbins的流程,以及所经历的检查。
图片描述
我们发现house of spirit的核心在于控制fake chunk和next chunk的size字段,而不在乎两个字段之间夹杂的内存。也就是说,只要我们可以任意写两块不相邻的内存,可以分别在两块内存构造size字段的值,在free后堆管理器会将这两段内存及其中间的内存视作一个chunk,而我们若可以通过malloc将这个伪造的chunk拿到,我们也就拿到了一块更大的连续内存。

程序是一个文字界面的购物菜单。Add可以增加步枪商品(创建和写堆块),show可以打印商品信息(读并打印堆块),Order函数可以清空购物订单(销毁所有堆块,free),Leave可以添加一个订单备注(往bss段写120字节),Stats函数可以打印本次购物的总体信息(读并打印几个bss段变量)。

程序的自定义结构体非常简单,可以很轻松的逆向出来,下面我主要对关键内存读写的部分进行追踪和分析。
下面结构体的每一个字段都有说明,主要是关于其读写说明,然后我也可以看到所有的订单都一个单链表的结构。
在add中可以创建Rifile结构体,其中desc和name字段在创建时有一次写入的机会,而在show函数中可以答应两个字段;在order函数中,会沿着next便利单链表逐一的free(next)。

下面是几个bss段变量。其中order_count在order函数中每free一个订单就会增加1,在stats中会被打印输出,即order_count是记录多少订单结算了。
obj_count每次add会增加1,调用order会清0,即obj_count是记录当前有多少订单,每次结算订单该变量都会清0。
order_note_ptr是一个指向bss段bss_write的指针,在leave中可以先bss_write写入最多120个字节,在stats中作为字符串指针打印。

漏洞在Add函数中。对Rifile结构体的name和desc字段都可以写入56字节,而两个字段的长度分别是27和25,已经发生了越界写,造成了堆溢出。这次越界写可以覆盖劫持next指针,劫持该指针可以通过调用show函数实现任意地址读。利用任意地址读,我们可以读got表泄露libc基地址。

图片描述
显然这个时候若,我们想要getshell,必须要实现任意地址写。我们知道next指针指向的地方会视作Rifile结构体,在调用order函数时会尝试free我们劫持的next指针。如果我们劫持next指针至一块我们精心构造的fake chunk,那么我们就能立刻add对构造的fake chunk写入原本我们没办法写入的地方。
那么显然我们只能在bss段去寻找目标了。我们仍然记得bss段有一个指针order_note_ptr,这是一个可以方便的进行读写的指针(只需调用stats和leave函数)。如果我们能够劫持就能实现任意地址,于是我们想当然是观察order_note_ptr指针的前后,看有没有我们可控的内存块,可供我们构造fake chunk。
幸运的是,order_note_ptr上方四字节有一个obj_count变量,而这个变量我们可以通过调用add使其自增,而Rifle结构体的chunk是0x40,因此fake chunk size的值是0x40(实际应该是0x41,因为PREV_INUSE此时是1)。但是我们要观察一下,以order_note_ptr做为mem指针向上偏移8字节是chunk指针,是否是8字节对齐的。我们发现order_note_ptr的地址是0x0804A2A8,减去8字节是0x0804A2A0,是8字节对齐的,因此可以通过地址对齐检查。
而我们可以往bss_write处开始写120个字节,所幸order_note_ptr距离bss_write并不远,两者相距0x18个字节,在fake chunk size的范围内。那么next chunk size的值比2 * SIZE_SZ大一些即可。
我们构造完成fake chunk后调用order函数,全部free,此时fastbin 0x40第一个chunk就是fake chunk,我们直接add出来,然后就可以劫持order_onte_ptr指针实现任意地址写了。
图片描述

现在我们实现任意地址写,并泄露了libc,我们利用house of spirit技术构造了fake chunk,并劫持了order_note_ptr指针实现了任意地址写,现在我们可以利用任意地址写劫持got表指针为system。并且代码中有一个绝佳的地方,是一个天然的shell。
如下图,如果我们劫持__isoc99_sscanf函数为system,那么我们可以很方便的控制变量s,进行RCE。
图片描述


文章来源: https://bbs.pediy.com/thread-277106.htm
如有侵权请联系:admin#unsafe.sh