参考内容
简介
本质上来说高版本off by null的目的就是为了同时过掉size vs prevsize
和bck-fd==P,fwd-bck==P
这两个check,利用手法主要是靠合理申请与释放堆块,在堆块中写入large bins、unsorted bins
指针以实现链表完整性。(旧版本仅仅检查被unlink
的chunk size与自身尾部的prev size
是否相等,故释放自身即可。新版额外检查这个chunk size与被free掉的chunk的prev size是否相等,chunk overlap必须要伪造后者,而前者不可控,故一般打法失效。)
large bin打法
核心思想是利用large bins的fd nextsize、bk nextsize
充当伪造chunk的fd、bk
,从而实现在large chunk内伪造size过检查。手法如下(均为逻辑视图,非内存视图):
- 先申请三个在同一
large bins
范围内但大小不同的chunk,中间和结尾用tcache
阻拦防止合并,全部free掉之后再申请大chunk
,通过大循环将这三个chunk全部放入同一个large bins。此时由于large bins机制,中间大小的chunk的fd、bk
与fd next size\bk nextsize
均相同,分别指向比自己小/大的chunk。
- 将
0x410 chunk(P)
取下,并伪造好fake chunk
的size。
此时large bin
如下所示:
- 再将
0x410 chunk
的bk(0x420 chunk)
取下并写其fd为该chunk(伪造bck-fd==P
)。此时large bins还剩下最后一个chunk(P-fd
),残留指针为头节点libc指针。
- 此时需要申请到
0x400 chunk
,再申请一个unsorted bins
范围的chunk(辅助堆块),先释放P-fd(0x400 chunk)
,再释放辅助堆块,这样二者都进入了unsorted bins
,且P-fd-bk
指向辅助堆块,可以覆盖为P的地址。
- 最后通过对
P
下面的tcache chunk
释放后申请进行off by null
,再释放P
实现unlink
与chunk overlap
。由于覆盖链表指针时需要覆盖两个字节,而后一个字节的高4位随机化,所以该方法只能实现1/16爆破。
由于off by null覆盖的最后一字节为0x00,所以需要在一开始加入padding chunk使被unlink的chunk本身地址形如
0x xxxx0xx
unsorted bin打法(非爆破)
不涉及large bins
。核心是通过释放与堆块合并来保留与获取所需的指针。
1.类似lagre bin
打法,先申请五个large bin
范围的chunk,同样需要预先申请padding chunk
使目标chunk地址形如0x xxxxxx00
。布局如下(tcache
堆块可根据leak与attack需求在图中位置上下随意增加,不影响指针关系):
再按顺序释放unsorted bin 1、3、5
,指针如下所示:
2.释放unsorted bin 2
,合并unsorted bin 3
(目的是为了保留其内部指针),此时经过unlink后unsorted bin 2+3
被插入链表头
3.申请大小略大于unsorted bin 2 的chunk
(这里需要提前控制好堆块大小unsorted bin 2>5>1
),使unsorted bin 1、5
被分入large bins
,同时覆盖原unsorted bin 3
处的size
为更大值。
被切割的unsorted bin 2+3
如图所示
large bin
如图所示
注意到一点:切剩下的堆块头与伪造的堆块头之间非常近,也就是说,它的地址与伪堆块头间的距离<0x100,二者的地址仅最后一个字节不同。
于是,我们可以申请回unsorted bin 1、5
以及切剩下的堆块
,分别释放(切剩下的+另一个堆块)到unsorted bins
中,再申请,再释放,这样就可以在P-fd
与P-bk
中踩出来了与P的地址仅1字节之差的地址,bypass unlink检查。
最后off by null
改unsorted bin 4
的fd in use
,再free
即可。
unsorted bins威力加强版(fastbin + malloc consolidate)
利用malloc consolidate
机制,可以在消耗堆块体积小得多的情况下完成类似攻击。
众所周知,malloc consolidate
会遍历fastbin
链表,并将链表中的free fastbin
放入unsorted bins
。如果有相邻的free fastbin
,还会进行合并。考虑到fastbin
本身体积很小,且可以很方便的申请-释放入tcache
构造fd
,因此可以考虑使用free fastbin
通过malloc consolidate
构造unsorted bins
双链表,同时合并chunk
以保留fd、bk
并伪造chunk size
,从而bypass检查。
2.31以上的glibc,其开头的tcache管理chunk大小为0x290
。这就意味着我们若直接申请一个0x70
大小的chunk,就会令下一个chunk地址为0x xxx300
。接下来,我们在0x100
的范围内申请8
个chunk,每个大小均为0x20
。申请一个0x4f0
的chunk,用于结尾释放入unsorted bins
并触发合并。再申请7
个0x20
的chunk填满tcache
,申请7
个0x60
的chunk填满tcache。此时内存布局如下:
接下来,我们可以先后释放chunk 6-2-8
,再释放chunk 1
,这些都会进入fastbin
中(这两步前后可以对调,但0x20 chunk
的链表关系需要注意,即size
内部的释放顺序需要在中间释放chunk 2
)。
申请一个>0x400
的chunk,直接free
,触发malloc consolidate
并合并这个chunk到top chunk
。
malloc consolidate
先会遍历0x20
的链表,构造出chunk 8-2-6
的0x20 unsorted bins
,然后会将0x70
的chunk释放进unsorted bins
并合并chunk 2
,制造出0x90
的chunk。该chunk的内存布局如下:
而此时0x20
的unsorted bins
变为chunk 8-6
。此时,可以申请0x90
的chunk,获取到上述chunk后覆盖0x20
这一size
为0x1e0
,以通过检查。
然后,申请0x20
的chunk,得到chunk 6
,改写残余的指向chunk 8
的bk
指向chunk 2
。(伪造P-fd-bk
)
再申请0x20
的chunk
,得到chunk 8
,再释放chunk 5
,释放chunk 8
(此时tcache
为chunk 8-5
),拿到chunk 8
,此时它的fd
指向chunk 5
,部分覆盖使其指向chunk 2
。(伪造P-bk-fd
)
释放并申请chunk 9
来off by null
,最后释放0x500
的chunk即可实现unlink
(unlink chunk 2
)。
碎碎念
最后一个方法由我本人提出,个人认为其一个巧妙之处在于利用malloc consolidate
在0x20的链表中踩出fd、bk
,同时利用小chunk地址差异小和便于申请释放来构造fd。另一个巧妙之处在于利用malloc consolidate
实现类似unsorted bins
的堆块合并来保存指针,同时利用size
差异导致的遍历顺序差异,实现先构造出完整的双链表再unlink
的效果。
感谢师傅们的blog,头一次有机会提出自己的手法orz