堆机制利用之fastbin

堆机制利用之fastbin

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

堆机制(fastbin等)

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

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

下面就是一个标准的chunk结构

堆机制利用之fastbin_第1张图片

该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占2~63,large bin占64~126

bin数组的结构大致如图

堆机制利用之fastbin_第2张图片

而我们的fastbinY数组为了追求效率,直接舍弃了对bk指针的管理,使得fastbin形成了一个单链表结构(而非一般的双链表),在进行添加删除操作时使用的是LIFO原则,结构大致如图

堆机制利用之fastbin_第3张图片
而fastbin的高效体现在什么地方呢?

默认情况下,对于size_t为4B的平台, 小于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之后,结构如图:

堆机制利用之fastbin_第4张图片
此时如果用户想再释放一次chunk1,程序会对单链表的头部chunk进行验证,发现用户对同一个chunk连续进行了两次释放操作,此时程序会报错并停止运行。

但是如果我们在释放chunk1之后释放一个chunk2,此时fastbin结构如图:

堆机制利用之fastbin_第5张图片
此时头节点就变成了chunk2,此时我们就可以对chunk1再一次进行释放操作,这就是fastbin攻击四类型的一种——double free攻击。

fastbin攻击方式

既然对于堆机制中的fastbin已经有了基本的了解,我们可以尝试看看对于fastbin的攻击方式。

1、double free

当我们申请了两块chunk,分别命名为chunk1和chunk2,然后依次释放chunk1和chunk2,此时fastbin结构如图

堆机制利用之fastbin_第6张图片
此时我们利用fastbin的特性,再次释放chunk1,此时会将fastbinY数组的fd指针指向chunk1,把chunk1的fd指针指向chunk2,导致最先进入fastbin的chunk1本应指向0x00的fd指针指向chunk2,此时chunk块结构如图:

堆机制利用之fastbin_第7张图片
此时我们如果再次申请一块大小与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的格式要求,并且修改的不是指定地址的内容,而是指定位置的前后内容,以下内容摘自ctf wiki

要想构造 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 的情况。

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

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的),这篇文章参考了很多大佬的博客以及ctf wiki,但终究是我自己一个字一个字码出来的。写完这篇文章后,我总算是对于堆区的一些机制有了一点最浅薄的理解。以前做堆区题就是看着大佬们的wp “读下去,记住它,“粤自盘古”呵!“生于太荒”呵!”然后一步一步地跟着调试,知其然而不知其所以然,直到今天,我才真正战胜自己,走出了对堆区纷繁复杂的漏洞机制抽丝剥茧的第一步。因为自己本身有点菜,所以文章也许会错漏百出,还请各位师傅不吝赐教。要是点赞比较多的话,我就尽快找时间把libc2.26及以后的版本(存在tcache机制)的堆区机制利用肝出来。毕竟“战胜恐惧最好的方法就是面对他!”,冬泳怪鸽诚不欺我~~~

你可能感兴趣的:(学习笔记,堆栈,pwn,数据结构)