欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

malloc源码分析(4)--free后mallocdouble free

发布时间:2024/3/24 48 豆豆
生活随笔 收集整理的这篇文章主要介绍了 malloc源码分析(4)--free后mallocdouble free 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

  • 测试代码
    • __libc_malloc
    • __libc_malloc调用_int_malloc
    • _int_malloc回到__libc_malloc
  • 如何利用double free
    • 测试代码
    • 测试代码2

之前我们从源码的角度分析了如何初始化heap并且从top chunk分配出了第一个块fast bin,那么这次我们结合free fastchunk来分配相同size的fastbin

测试代码

#include<malloc.h> int main(){void *p=malloc(0x18);*(int *)(p+8)=0xdeadbaaf;free(p);p=NULL;void *p1=malloc(0x18);return 0; }

其实代码很简单,我们主要就是跟踪一下从freechunk里面malloc出来究竟有哪些check
大概运行到这里吧,然后我们跟进

__libc_malloc

我们首先看看现在的main_arena

可以看到对应大小有一个空闲块,并且flags是0代表有fastchunks
同时__malloc_hook为NULL,只要我们不设置,初始化后他就一直是NULL.所以我们这里不会去调用

获取ar_ptr并且进入_int_malloc

__libc_malloc调用_int_malloc


这个函数两个作用

  • 如果需要的size太大,就返回0
  • 正常请求就转化成具体需要的size(需要考虑header)


那么多大算太大呢

internal_size_t也就是8,minsize也就是0x20,所以就是(unsigned)-0x200

具体为什么这个值我也不清楚,但一般情况下肯定都是满足的
大部分都类似于之前提到的malloc
这里因为我们大小确实满足,所以会进来,之前由于没有初始化所以进不来

非常熟悉的操作,寻找到对应的fastbiny的地址

这里我们肯定会执行一次因为do while,如果victim==NULL代表里面确实没有就跳出,然后while的成立的条件其实是写入失败,因为写入成功最后会返回victim,那么就相当于是false,所以一般我们也会成功,这里写while可能会涉及多线程,但单线程的时候如果有free chunk就相当于我把victim->fd写到fastbiny里面,就相当于拿出来一个

这里其实是检查fastbiny的大小是否和我们需要的size匹配,一般来说是匹配的,如果失败说明这个fastbin链表被修改了

额这个是源码里面的

结果我直接跳到这里来了

感觉是这个优化的有点奇怪,我单步执行一下,好像执行了void *p=chunk2mem(victim);
然后这个allo_perturb和free_perturb都是一样的,都不会执行

那么就回到了__libc_malloc

_int_malloc回到__libc_malloc

这里我们已经获取到了要分配的区域,所以不会进去

解锁

简单检查一下

就回来了

可以看到我们这里malloc的就是刚刚释放的,并且里面还有数据
所以一般提倡malloc后立马memset清空

如何利用double free

测试代码

#include<malloc.h> int main(){void *p=malloc(0x18);void *p1=malloc(0x18);free(p);free(p1);free(p);void *p2=malloc(0x18);*(size_t*)p2=0xdeadbaff;return 0; }

回到我们上次提到的double free,为了避免free check double free的情况,我们中间需要随便free掉一个heap,然后就形成了这样环形的情况

我们首先malloc一个
大家知道由于malloc不会清空heap内容,所以我们现在fastbiny保存的就应该是0x405020,然后这个时候我们修改一下fd的指针

可以看到变成这个样子了

所以我们如果再malloc三次的话,第三次就应该可以malloc到我们需要任意写的地方
为什么可以这样呢?
其实我们通过刚才简单的free malloc可以看出,我们fastbiny他是以0作为结束标准的,他不会去记录你到底进去了几个出来了几个,只要里面不是0,我认为就还有,还可以分配,所以当我们修改了链之后,他就会乖乖去跟着fd去遍历
而一般来说fd都是可以控制的,它属于我们的data区域

那么我们接着攻击,看可以可以malloc三次

测试代码2

因为中间两次的malloc其实是没什么用的,所以我就没保留指针

#include<malloc.h> int main(){void *p=malloc(0x18);void *p1=malloc(0x18);free(p);free(p1);free(p);void *p2=malloc(0x18);*(size_t*)p2=0xdeadbaff;malloc(0x18);malloc(0x18);void *p3=malloc(0x18);return 0; }

如愿以偿的修改成了deadbaff,我们执行一下

报错,奥段错误,确实,这个地方不一定有内容,我们指向别的吧

这里其实我们要获取__malloc_hook就要知道libc加载地址,但这里我遇到了点问题
dlopen是可以用来获取so加载地址,类似于windows下的GetProcAddr,但这里如果我用的不是系统自身版本的libc就会报错,暂时还不知道为什么
这里我们先写死吧

这里可以看到我具体的加载地址

那么对于我就是这样的

#include<malloc.h> int main(){void *p=malloc(0x18);void *p1=malloc(0x18);free(p);free(p1);free(p);void *p2=malloc(0x18);*(size_t*)p2=0x7ffff7fcbb10;malloc(0x18);malloc(0x18);void *p3=malloc(0x18);return 0; }

我们执行到这里可以看到,bins已经被修改到malloc_hook,也就是说我们malloc的第三个就在malloc_hook附近


卧槽,报错了??,我们跟踪一下

一直到_int_malloc都没什么问题

问题还是出现在了这里

由于victim就是我们当前将要分配的区域,他会检查这个大小是否匹配,如果不匹配就不分配
所以我们先要绕过这个大小检查
这个也是fastbin attack里面需要特别构造的一点,就是大小匹配
注意到malloc上面这一块,有一个比较特殊的偏移

可以看到有个7f的区域

那么我们再去看看chunksize的实现

#define chunksize(p) ((p)->size & ~(SIZE_BITS))


size_bits其实就是4+2+1=7
所以相当于就是

fastbin_index的实现呢

左移4位减去2,刚好就是5,而5对应的大小就是0x70,所以我们要保证最后分配的块实际大小是0x70
为什么这里0x78也可以呢,还记得我说idx是/16嘛,所以你多一点没关系,毕竟我是整除,其实我个人觉得如果毕竟chunksize(victim)==size最好
因为这种不严格的判断才导致我们可以利用0x7f来绕过,而且在实际中,一般块的大小也只会收到1,2,4标记位的影响,我们chunksize已经去掉了,不知道这里为什么还要多此一举反而可以让我们绕过

接下来就是修改一下payload,因为要保证获取0x70

#include<malloc.h> int main(){void *p=malloc(0x68);void *p1=malloc(0x68);free(p);free(p1);free(p);void *p2=malloc(0x68);*(size_t*)p2=0x7ffff7fcbb10;-0x23malloc(0x68);malloc(0x68);void *p3=malloc(0x68);return 0; }

首先运行一下,可以看到成功分配

我们再调试一下
这个就是__malloc_hook上面的那个地址

bytes是我们需要的,nb是实际的,idx就是实际需要的fastbin对应的索引

当前的fastbiny地址和空闲块地址



victim就是我们这里的pp
这里因为刚好符合我们的涉及就准备返回

回到主函数,确实也被修改了

这才是完整的malloc_hook对应的heap

我们找找malloc_hook的偏移0x23-0x10=0x13

这里我们测试就写个deadbaaf,实际上需要写一个one_gadget

所以总的大概double free就这样啦

总结

以上是生活随笔为你收集整理的malloc源码分析(4)--free后mallocdouble free的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。