Tcache_Stashing_Unlink_Attack就是calloc的分配不从tcache bin里取chunk,calloc会遍历fastbin、small bin、large bin,如果在tcache bin里,对应的size的bin不为空,则会将这些bin的chunk采用头插法插入到tcache bin里。首先,我们来看一下glibc 2.29的源码。
1 | /* |
如上,我们看到,从small bin中取出最后一个chunk的时候,对双向链表做了完整性的检查,然而,后面将剩余chunk放入tcache bin的时候却没有这个检查。然后,bck->fd = bin这句代码,可以将bck->fd处写一个main_arena地址。如果我们可以控制bck,那么就能实现任意地址处写一个main_arena的地址。同理,如果我们能够控制small bin的bck,并且保证vuln_addr->fd = bck,那么就能分配到vuln_addr处。
为了加深理解,我们从两道题来巩固一下。
hitcon_ctf_2019_one_punch
首先,检查一下程序的保护机制

然后,我们用IDA分析一下
Delete功能没有清空指针,可以double free。

以及UAF编辑

Add功能使用的是calloc分配,并且size的大小不在fastbin范围,因此用不了fastbin attack。

后门函数里使用malloc分配

但是要想利用后面函数,就得绕过if的检查,而此处是一个堆地址,我们不能直接修改,我们可以利用Tcache_Stashing_Unlink_Attack将此处写一个main_arena地址,进而可以绕过if,执行malloc从tcache bin里分配到目标处。此处,我们的目的仅仅是往那个堆地址处写一个大于6的数据,在Tcache_Stashing_Unlink_Attack时,会从small bin里取chunk到tcache bin,直到tcache bin填满,但是如果我们伪造了bck,第二次遍历的时候就会发生错误,因为目标处我们不可控。因此,我们只需要让其只进行第一次的遍历,那么,我们就得事先将对应的tcache bin里填满6个。为了绕过对small bin最后一个chunk的完整性检查,我们不能伪造最后一个chunk的bck,而应该伪造倒数第二个chunk的bck。因此,我们需要保证在small bin里有两个chunk。
然后通过calloc取出最后一个chunk时,发生Tcache Stashing,从而将目标处写上一个main_arena地址。
1 | #0 |
接下来,泄露堆地址和glibc地址
1 | for i in range(6): |
接下来,我们需要得到两个small bin。首先,得到第一个0x90的small bin
1 | #从unsorted bin里切割0x190,剩余0x90 |
接下来,得到我们第二个small bin。
1 | for i in range(7): |

现在,我们要修改倒数第二个small bin的bk为目标地址,然后实施tcache stashing attack
1 | #修改倒数第二个头chunk的bk,fd不变 |
现在,我们就可以调用后面函数了,那么通过UAF伪造tcache bin的next指针,分配到目标处。我们可以改写malloc_hook或者free_hook。如果没有开沙箱的话,我们直接改写为one_gadget即可,如果开啦,我们改为add rsp,0xXX,使得栈进入我们可控的buf区

1 | #coding:utf8 |
RedPacket_SoEasyPwn1
此题与上一题差不多,直接贴上exp
1 | #coding:utf8 |