Tcache Attack(3)

Tcache stashing unlink attack:

这个利用手法就比前面三种要复杂一点,但是并不是非常复杂,它利用的是在small bin中如果有chunk被分配走后(利用calloc分配的话就可以避开tcache进行分配)在tcache中对应大小的链表内若有空闲位置(少于7个),则会将small bin中的链表按照顺序挂进tcache的对应链表内,由于这个small bin转tcache的过程中只对small bin链表中的第一个元素进行了完整性检查,而对其顺位下的其chunk却没有进行检查,所以我们可以通过修改第一个chunk的bk指针来将我们目标地址的内存挂进tcache中。

那么我们可以总结出这种利用手法的一个思路:

  1. 拥有一个未满的tcache列表(大小要与small bin的大小匹配)
  2. 在small bins中挂进两个以上的chunk
  3. 修改small bin中非第一个chunk的bk指向目标地址
  4. 利用calloc等函数的机制绕开tcache分配small bins中的chunk
  5. 触发tcache和unlink机制将目标内存挂进 tcache中

上面的这段阐述可能有点拗口也不好理解,下面给出wiki上的这个例子,用gdb跟踪运行一下就能理解了(做了一些简化):

#include <stdio.h>
#include <stdlib.h>

int main(){
    unsigned long stack_var[0x10] = {0};
    unsigned long *chunk_lis[0x10] = {0};
    unsigned long *target;

    printf("stack_var addr is:%p\n",&stack_var[0]);
    printf("chunk_lis addr is:%p\n",&chunk_lis[0]);
    printf("target addr is:%p\n",(void*)target);
    
    stack_var[3] = (unsigned long)(&stack_var[2]);

    for(int i = 0;i < 9;i++){
        chunk_lis[i] = (unsigned long*)malloc(0x90);
    }

    for(int i = 3;i < 9;i++){
        free(chunk_lis[i]);
    }


    free(chunk_lis[1]);
    free(chunk_lis[0]);
    free(chunk_lis[2]);


    malloc(0xa0);

    malloc(0x90);
    malloc(0x90);

    chunk_lis[2][1] = (unsigned long)stack_var;


    calloc(1,0x90);

    
    target = malloc(0x90);   

    
    return 0;
}

这个程序的流程稍显复杂,下面来捋一下:

首先是定义了一个stack_var[0x10]的数组作为我们需要完成分配的目标地址,然后定义了一个 chunk_list[0x10]的指针数组,还有一个target指针。

然后将stack_var[3] (也就是这个将被作为fake_chunk的bk参数)置为stack_var[2]的地址。

接着是两个for循环:第一个for循环创建9个0x90大小的chunk,并用这些malloc的指针填充进chunk_list之中(所以chunk_list里面装着的就是这个9个chunk的指针)

第二个for循环释放了chunk_list的后面6个chunk,之后在循环外按1,0,2的顺序释放了其他三个chunk。

之后连续申请(malloc)一个0xa0,两个0x90的chunk。

最后修改chunk_lis[2] [1] 为stack_var的地址后申请一个0x90的chunk(calloc)

让我们进入gdb跟踪一下:

首先将断点下在12行,看一下分配给我们前三个变量的地址:

在这里插入图片描述
然后修改stack_var[3]的值:
在这里插入图片描述
这里说一下为什么要改这个东西,因为如果将这段内存作为small bin chunk来看的话,这个红框位置上就是bk指针的参数,而后面它作为fake_chunk挂进tcache的时候后面如果bk没有置为一个类似循环链表的情况的话,就会出现错误,所以这个bk是一定要指向自身的。

然后我们执行完两个for循环看一下bins中的情况:
在这里插入图片描述
可以看见tcache里面有了6个free_chunk,但是对应链表还没有满(7个是最大数量)

然后我们再按1,0,2的顺序释放剩余的三个chunk,再看一下bins中的情况:
在这里插入图片描述
可以看见chunk 1作为最后一个tcache free_chunk挂进了tcache中,而chunk 0和chunk 2由于tcache满了,只能挂进unsortedbin当中,此时我们再次步进,让系统为我们分配一个0xb0大小的内存空间。

由于tcache中没有大小匹配的chunk,unsortedbin中也没有,所以程序只能在top chunk里面割一块出来给我们。而我们前面申请释放的那些chunk也是保持原状,只有一个地方出现了变动:

unsortedbin由于机制问题,在这一次分配后会将自己链表内的chunk归于对应large bin或small bin中,而这个例子中的unsortedbin中的两个chunk就会被挂进small bin中:
在这里插入图片描述
之后我们执行第34行代码将chunk_lis[2] [1]修改为我们的目标地址:stack_var的地址。

这里说一下这个chunk_lis[2] [1]是什么意思:其实就是chunk_list中的第3个元素的第2个地址上的内容。这一步也就是完成了上面说到的修改small bins中最后一个chunk的bk参数。来看一下效果:
在这里插入图片描述
可以发现bk已经指向了我们的目标地址并且由于我们前面改了fake_chunk的bk指针完成了一个像“收口”一样的操作,之后我们通过calloc来分配一个0x90的chunk,而由于calloc并不会在tcache中分配chunk,所以会直接从small bin中分配。

之后关键的就来了:由于前面我们malloc了两个0x90的chunk,tcache它的链表又不满了,所以这一次分配后会将small bin中的剩余的chunk挂进tcache之中,而small bin是FIFO的进出原则,所以chunk 0就被分配出去了,剩下的chunk 2和它后面连着的fake_chunk就被挂进tcache了。
在这里插入图片描述
由于这个fake_chunk正好在tcache第一个位置上,所以我们只要再分配一个0x90的chunk就可以再次启用并控制这个stack_var上的内存
在这里插入图片描述

可以看见这个stack_var已经被在启用,指针也赋给了target。这样我们就完成了一次tcache stashing unlink。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值