各种bin在多种情况下漏洞利用的理论和实践
先看一下main_arena中的大致结构:
largebin attck
此图不完全正确,其中largebin里的chunk如果是小组中最前面的那个chunk,并且大组中只有一个小组,bk_nextsize和fd_nextsize就都指向自己,且链表当中最后一个chunk的bk和最前面的chunk的fd指针指向头部,如果是小组中非最前面的chunk,则bk_nextsize和fd_nextsize都为0
当往largebin中放入chunk的时候,会遵循以下流程:
1.首先判断放入chunk的大小范围属于哪一大组,如果此大组此前没有chunk存在那么此时fwd(虽然fwd=bck->fd,但是因为大组为空,要保证链表循环,所以fwd和bck指针一样都指向头部指针)和bck指针都指向头部指针,然后再设置放入的chunk的fd_nextsize和bk_nextsize指向自己,等待第5步设置bk指针和fd指针,如果大组里原本就有chunk存在,则进入下一步
2.准备设置遍历指针bck和fwd,将bck指向这一大组的头部指针,也就是largenbin[i],然后将fwd指针指向bck->fd
3.如果放入的chunk的size小于bck->bk(也就是这个大组当中最小的chunk),则将fwd指向头部指针,bck指向最小的chunk,接着设置其的fd_nextsize和bk_nextsize以及前后的fd_nextsize和bk_nextsize(共需设置4个指针),等待第5步设置bk指针和fd指针
4.如果放入的chunk的size小于fwd(也就是此组中最大的那个chunk)的size,则开始利用fwd遍历(fwd=fwd->fd_nextsize)直到找到能按照大小规范顺序的位置插入新的小组或fwd的size等于放入chunk的情况为止,如果是等于放入chunk的情况,则此时fwd定位成功(定位的位置为和放入的chunk的size一样大小的小组的第一个chunk的fd指针),然后再设置bck=fwd->bk指针(也就是设置bck为指向和放入的chunk的size一样大小的小组的第一个chunk的指针),等待第5步设置bk指针和fd指针。如果是按照规范顺序的位置插入一个新的小组则和第二步一样插入后设置好4个指针,等待第5步设置bk指针和fd指针
5.将放入chunk的fd指针指向fwd,bk指针指向bck,fwd->bk指向放入的chunk,bck->fd也指向放入的chunk;
总结一下,插入一个chunk之后,依据情况会改变8或4个指针,8个指针的情况是放入chunk的4个指针和前面chunk的bk和bk_nextsize以及后面chunk的fd和fd_nextsize
接着说一下漏洞部分
先说一下漏洞利用的最终结果,就是会将某一致两个地址的的数据变成某一个在largebin里的chunk的地址
接下来利用的思路就是通过修改一个largebin中的chunk1的区域,来修改他的一些信息(size)和指针(fd,bk,bk_nextsize,fd_nextsize),因为插入到largebin里主要使用的是fd和bk的nextsize指针,所以我们使其的bk_nextsize指针指向我们想写的地址-0x20,然后我们要知道现在的fwd始终指向此大组的第一个chunk,而bck等于fwd->bk,以及这里是不会检查fd和bk和bk_nextsize和fd_nextsize指针指向的chunk是否合规,所以我们可以任意修改。此时插入一个比刚刚在largebin里更大的chunk2,会在chunk1的前面插入chunk2,从而使chunk2的fd_nextsize和fd指向chunk1,bk和bk_nextsize指向chunk1原本bk和bk_nextsize指针指向的chunk,也就是bk_nextsize会指向我们想写的地址-0x20,bk会指向头部指针(如果largebin原本只有chunk1的话),然后就会将chunk2的bk_nextsize指向的chunk的fd_nextsize变为chunk2。接着而由上面那个黑字,所以我们更改chunk1的bk指针为chunk2,就能更改bk指针指向的chunk的fd指针为chunk2
总结一下:插入的时候会把几个指针变为chunk2,1.chunk1的bk_nextsize 2.chunk1的bk_nextsize指向的chunk的fd_nextsize指针(程序正常的话应该是指向最小的chunk的fd_nextsize) 3.chunk1的bk指针指向的chunk的fd指针(程序正常的话应该是让头部指针的fd指针指向chunk2)4.chunk1的bk指针
而我们就是利用其中的2和3两点造成了任意地址写入一个chunk地址值,当然 这是创建一个小组的情况,还有其他情况例如放入已存在的小组的情况和在中间创建小组的情况也可以利用,只要被构造的chunk和新放入的chunk有联系就行,例如将新插入的chunk即将要生成的fd指针指向的chunk的bk指针改为要写入的地址-0x10,还有如新插入的chunk如果比最小组还小的话,就修改最大的那个chunk的bk_nextsize为要写地址的-0x20
Large bin attack利用条件
1.可以修改一个large bin chunk(如上面所说的chunk1)的data (UAF)
2.在放入新的chunk进入largebin的时候,要保证新进来的chunk和chunk1有联系,也就是会相互修改指针
tcachebin attack
tcachebin中有几个性质以及漏洞点
1.64位下tcachebin中有64个组,64位下记录了size从0x20到0x410的chunk,每个组相差0x10,大于0x410的chunk会先放入unsortedbin中
32位下目前只知道不能存放8结尾的chunk
2.tcachebin中每个组最多有7个chunk,当一个组中chunk满了之后又有新的chunk想要进来,会依据范围进入fastbin或unsortedbin
3.tcachebin只有fd指针,且tcachebin的头部指针的fd指向在tcachebin中的第一个chunk,而最后一个chunk并没有指向头部指针(和fastbin一样)
4.当要分配时,例如要求分配size1的chunk,并且如果此时tcachebin相应的size组为空时,就会按照低版本malloc的顺序规则去其他bin里查找,并且返回所有精确的size1(不会返回非size1精确大小的chunk来填充tcachebin)填充到tcachebin直到满7个或返回所有的size1大小的chunk。接着进入到tcachebin里,然后再在tcachebin里取出。
5.Doublefree 在2.27~2.29版本中tcachebin相对于fastbin的安全检查更少,甚至能直接利用double free连续free(a)多次,相比于fastbin要在两次连续的free(a)中穿插一个free(b)更简单。
6.tcache house of spirit 此漏洞是因为tcachebin(目前只确定2.27有这个漏洞)在free一个chunk的时候,只会检测这个chunk的size是否存在,因此可以伪造一个fake chunk,free它以此来让他进入到bin链当中。
7.tcache stashing unlink attack 此类型的利用中,calloc(用此函数获取的malloc不会从tcachebin中取得)的chunk的fd指针指向的下一个chunk(此chunk要求size要存在且合法,也就是要大于等于0x20)的bk指针必须指向calloc的chunk才会被填充进入tcache,只有一个chunk需要检测,后面的chunk在第一个chunk检测成功之后可直接进入tcachebin
fastbin attack
先说一下性质:
1.fastbin有10个组,但只有前7个被用来储存chunk(32位是0x100x40,以0x08间隔;64位是0x200x80,以0x10间隔),每个链表可以有无限chunk
2.fastbin只有fd指针,且fastbin的头部指针的fd指向在fastbin中的第一个chunk,而最后一个chunk并没有指向头部指针
要让fakechunk进入到fastbin里,需要满足以下条件
1、fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理
IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的,这个标志位位于size低二比特位
2、fake chunk 地址需要对齐, MALLOC_ALIGN_MASK
因为fake_chunk可以在任意可写位置构造,这里对齐指的是地址上的对齐而不仅仅是内存对齐,比如fake_chunk的pre_size所在地址就应该位0xXXXX0或0xXXXX8。(32位和64位相同)
3、fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐
fake_chunk如果想挂进fastbin的话构造的大小就不能大于0x80,关于对齐看第四点,并且在确定prev_size的位置后size所在位置要满足堆块结构的摆放位置
4、fake chunk 的 next chunk 的大小不能小于等于 2 * SIZE_SZ(在 32 位系统上,SIZE_SZ通常是 4 字节,而在 64 位系统上,则是 8 字节),同时也不能大于等于av->system_mem,即128kb
fake_chunk 的大小,大小必须是 2 * SIZE_SZ 的整数倍。例如64位程序的size为0x48的话就会报错。32 位系统中,SIZE_SZ 是 4(32位程序下在用malloc创建的时候,无法创建0x18size的chunk,但是可以自己创造一个0x18size的fakechunk,但是在2.27版本之后通过实践得知似乎不能存放8结尾的chunk,会报错invalid size,不知道为什么);64 位系统中,SIZE_SZ 是 8。最大不能超过av->system_mem,即128kb。
5、fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况
这个检查就是fake_chunk前一个释放块不能是fake_chunk本身,如果是的话_int_free函数就会检查出来并且中断。