堆溢出利用之Fastbin

前半部分 :基于libc2.23(无tcache)

堆机制(fastbin等)

想要了解堆机制利用方法必须要先了解堆的基本机制以及结构

目前主要使用的内存管理库是ptmalloc,而在ptmalloc中,用户请求的空间由名为chunk的数据结构表示

下面就是一个标准的chunk结构
1.jpg
该chunk中,prev_size参数为前一chunk的大小,size参数为该chunk的大小,而p参数(pre_insue)为标志位,标志前一个chunk的使用情况。而上述的三个参数组成了chunk的header部分,该部分一般不用被用户直接访问

用户能够访问的空间为mem部分,如果一个chunk正被使用,则data部分为用户存储的内存空间,此时fd、bk指针并无实际意义。如果一个chunk未被使用,则mem部分的fd与bk存储的分别是上一个和下一个未被使用的chunk的地址。而这样一个由未被使用的chunk组成的链表被称为bin。

一般而言,不同大小的free chunk会被分类到不同的bin中,而bin的类型可以被分为fast bin,small bin,large Bin和unsorted bin。其中,fast bin的操作效率较高,为单向链表,其它的都是双向链表。较高的操作效率意味着较低的安全性,所以fastbin机制产生的漏洞也是堆区漏洞的最重要组成部分之一。

诸多的bin链由指针进行管理与保存,数组里头装的就是不同大小的Bin链的头尾结点指针。

fastbinY数组:大小为10,为fastbin的专用数组
bins数组:大小为129,其中unsorted bin占1,small bin占263,large bin占64126

bin数组的结构大致如图
1.png
而我们的fastbinY数组为了追求效率,直接舍弃了对bk指针的管理,是fastbin形成了一个单链表结构(而非一般的双链表),在进行添加删除操作时使用的 是LIFO原则,结构大致如图。
2.png
而fastbin的高效体现在什么地方呢?

默认情况下,对于size_t为4B的平台,小于64B的chunk分配请求;对于size_t为8B的平台,小于64B的chunk分配请求;对于size_t为8B的平台,小于128B的chunk分配请求,程序会根据所需的size首先到fastbin中去寻找对应大小的bin中是否包含未被使用的chunk,如果有,则直接从bin中返回chunk。而释放chunk时,也根据chunk的size参数计算fastbin中对应的index,如果存在对应的大小,就将chunk直接插入对应的bin中。

32位平台size_t长度为4字节,64位平台的size_t长度可能是4字节,也可能是8字节,64位linux平台size_t长度为8字节

而且为了追求效率,fastbin不仅使用单链表进行维护,由fastbin管理的chunk即使在被释放后chunk的p参数也不会被重置,而且在释放时只会对链表指针头部的chunk进行校验。

以下图为例,在释放掉chunk1之后,结构如图:
3.png
此时如果用户想再释放一次chunk1,程序会对单链表的头部chunk进行验证,发现用户对同一个chunk连续进行了两次释放操作,此时程序会报错并停止运行。

但是如果我们在释放chunk1之后释放一个chunk2,此时fastbin结构如图:
4.png
此时头结点就变成了chunk2,此时头结点就变成了chunk2,此时我们就可以对chunk1再一次进行释放操作,这就是fastbin攻击四类型的一种——double free攻击

fastbin的攻击方式

1、double free

当我们申请了两块chunk,分别命名为chunk1和chunk2,然后一次释放chunk1和chunk2,此时fastbin结构如图
5.png
此时我们利用fastbin的特性,再次释放chunk1,此时会将fastbinY数组的fd指针指向chunk1,把chunk1的fd指针指向chunk2,导致最先进入fastbin的chunk1本应指向0x00的fd指针指向chunk2,此时chunk块结构如图:
6.png
此时我们如果再次申请一块大小与chunk1大小相同的堆块,我们就能从fastbin中取出chunk1,并对chunk1的fd指针进行修改。注意此时当chunk1的fd被修改后,整个单链表的表尾就指向了修改后的fd,此时先连续malloc两次,把chunk2与chunk1一次取出,在下一次malloc的时候,我们就可以在我们指定的地址申请堆块,间接实现了任意地址写操作。(前提:需要操作目标地址使得size通过fastbin的校验)

2、house of spirit

其实感觉house of spirit和double free方法大差不差,只不过更仔细地说明了fake chunk的格式要求,并且修改的不适指定地址的内容,而是指定位置的前后内容。

要想构造fastbin fake chunk,并且将其释放时,可以将其放入到对应的fastbin链表中,需要绕过一些必要的检测,即

①fake chunk的ISMMAP位不能为1,因为free时,如果是mmap的chunk,会单独处理
②fake chunk地址需要对齐,malloc_align_mask
③fake chunk的size大小需要满足对应的fastbin的需求,同时也得对齐。
④fake chunk的next chunk大小不能小于2*SIZE_SZ,同时也不能大于av->system_mem。
⑤fake chunk对应的fastbin链表头部不能是该fake chunk,即不能构成double free的情况。

可以看出,想要使用该技术分配到指定的地址,其实并不需要修改指定指定地址的任何内容,关键是要能够修改指定地址前后的内容使其绕过对应的检测。

3、alloc to stack

原理与前二者基本相同,还是戒尺fastbin链表中的chunk的fd指针(除了double free,还可以用堆溢出等技巧),从而把chunk分配到栈上,从而控制栈上的关键数据(如校验值或者关键的返回地址),当然同时需要栈上存在有满足条件的size值。

4、arbitrary alloc

arbitrary alloc其实alloc to stack是完全相同的,唯一的区别是分配的目标不再是栈中。事实上只要满足目标地址存在合法的size域(这个size域是构造的,还是自然存在的都无妨),我们可以把chunk分配到任意的可写内存中,比如bss、heap、data、stack等等。

写到这里,这个fastbin机制的漏洞利用方式已经基本上讲完了(当然是基于libc2.23的)。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!