Double free(hows2heap)

1.fastbin_dup

fast bin

fast bin主要是用来存放一些小的内存,使用fastbin可以提高小内存的分配效率,在默认情况下,对于SIZE_SZ为4B(32位)的平台,小于64B(字节)的chunk分配请求,和对于SIZE_SZ为8B(64位)的平台,小于128B的chunk分配请求首先会在fast bin中查找是否有所需大小的chunk存在,如果存在,就会直接返回内存资源,如果不存在,才会到其他bins中去查找。
fastbins 可以看成一个后进先出的栈,使用单链表来实现,通过 fastbin->fd 来遍历.

double free

double free 其实就是同一个指针free两次,然后通过伪造chunk来欺骗操作系统,以达到修改内存的目的。在fast bin中,是通过单链表的形式来管理chunk,并且在free时会进行检查,因此我们不能连续free两次同一个chunk,但是我们可以在两次free之间再增加一个free,从而绕过检查,然后再malloc三次,就在同一个地方malloc两次,也就是两个指向同一个地址的指针,下面用一简单的程序来说明这个原理,代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
    fprintf(stderr, "这个例子演示了 fastbin 的 double free\n");
 
    fprintf(stderr, "首先申请了 3 个 chunk\n");
    char* a = malloc(8);
    strcpy(a, "AAAAAAAA");
    char* b = malloc(8);
    strcpy(b, "BBBBBBBB");
    char* c = malloc(8);
    strcpy(c, "CCCCCCCC");
 
    fprintf(stderr, "第一个 malloc(8): %p\n", a);
    fprintf(stderr, "第二个 malloc(8): %p\n", b);
    fprintf(stderr, "第三个 malloc(8): %p\n", c);
 
    fprintf(stderr, "free 掉第一个\n");
    free(a);
 
    fprintf(stderr, "当我们再次 free %p 的时候, 程序将会崩溃因为 %p 在 free 链表的第一个位置上\n", a, a);
    // free(a);
    fprintf(stderr, "我们先 free %p.\n", b);
    free(b);
 
    fprintf(stderr, "现在我们就可以再次 free %p 了, 因为他现在不在 free 链表的第一个位置上\n", a);
    free(a);
    fprintf(stderr, "现在空闲链表是这样的 [ %p, %p, %p ]. 如果我们 malloc 三次, 我们会得到两次 %p \n", a, b, a, a);
     
    char* d = malloc(8);
    char* e = malloc(8);
    char* f = malloc(8);
    strcpy(d, "DDDDDDDD");
    strcpy(e, "EEEEEEEE");
    strcpy(f, "FFFFFFFF");
    fprintf(stderr, "第一次 malloc(8): %p\n", d);
    fprintf(stderr, "第二次 malloc(8): %p\n", e);
    fprintf(stderr, "第三次 malloc(8): %p\n", f);
}

这里程序首先分配三个大小为8的chunk,并且给他们赋值为“A”,“B”,“C”,这里因为有一个字节对齐,所以最少会分配8字节的块,并且堆的size的大小都会对齐为16的倍数,所以这里分配了0x10的块大小
在这里插入图片描述
在这里插入图片描述
第一次free后,我们可以看到第一个堆块已经放到fast bin中去了,这里的P位还是1是因为free后放到fasat bin中,并不会将P位进行置0
在这里插入图片描述接着free第二个堆块,可以看到第二个堆块也放入fast bin中了,这时fast bin中其实已经有一个LIFO的链表存在,chunk b的fd指针指向chunk a。
在这里插入图片描述
在这里插入图片描述
三次malloc后我们看到两个chunk a和chunk b已经形成了一个循环链表,同时在fast bin中会有三个chunk。
在这里插入图片描述
在这里插入图片描述

这时候我们再去malloc两次,就会将前面两个chunk申请出来,这样chunk a在两个都可以利用了,我们修改申请的chunk d,就可以修改在fast bin中的fd指针,指向一个我们需要的内存位置,
在这里插入图片描述
这个程序中只申请了一次,就是chunk f,然后我们可以看到chunk a的位置被改为“FFFFFF”了

2.fastbin_dup_into_stack(double free in stack)

前面写了unsorted bin和fast bin的double free知识,现在开始写一下伪造堆块并且分配出来的姿势,下面的程序的功能是在栈上伪造一个堆块,然后申请出来,代码如下:


```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
    fprintf(stderr, "这个例子拓展自 fastbin_dup.c,通过欺骗 malloc 使得返回一个指向受控位置的指针(本例为栈上)\n");
    unsigned long long stack_var;
 
    fprintf(stderr, "我们想通过 malloc 申请到 %p.\n", 8+(char *)&stack_var);
 
    fprintf(stderr, "先申请3 个 chunk\n");
    char* a = malloc(8);
    strcpy(a, "AAAAAAAA");
    char* b = malloc(8);
    strcpy(b, "BBBBBBBB");
    char* c = malloc(8);
    strcpy(c, "CCCCCCCC");
     
    fprintf(stderr, "chunk a: %p\n", a);
    fprintf(stderr, "chunk b: %p\n", b);
    fprintf(stderr, "chunk c: %p\n", c);
 
    fprintf(stderr, "free 掉 chunk a\n");
    free(a);
 
    fprintf(stderr, "如果还对 %p 进行 free, 程序会崩溃。因为 %p 现在是 fastbin 的第一个\n", a, a);
    // free(a);
    fprintf(stderr, "先对 b %p 进行 free\n", b);
    free(b);
 
    fprintf(stderr, "接下来就可以对 %p 再次进行 free 了, 现在已经不是它在 fastbin 的第一个了\n", a);
    free(a);
 
    fprintf(stderr, "现在 fastbin 的链表是 [ %p, %p, %p ] 接下来通过修改 %p 上的内容来进行攻击.\n", a, b, a, a);
    unsigned long long *d = malloc(8);
 
    fprintf(stderr, "第一次 malloc(8): %p\n", d);
    char* e = malloc(8);
    strcpy(e, "EEEEEEEE");
    fprintf(stderr, "第二次 malloc(8): %p\n", e);
    fprintf(stderr, "现在 fastbin 表中只剩 [ %p ] 了\n", a);
    fprintf(stderr, "接下来往 %p 栈上写一个假的 size,这样 malloc 会误以为那里有一个空闲的 chunk,从而申请到栈上去\n", a);
    stack_var = 0x20;
 
    fprintf(stderr, "现在覆盖 %p 前面的 8 字节,修改 fd 指针指向 stack_var 前面 0x20 的位置\n", a);
    *d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
     
    char* f = malloc(8);
    strcpy(f, "FFFFFFFF");
    fprintf(stderr, "第三次 malloc(8): %p, 把栈地址放到 fastbin 链表中\n", f);
    char* g = malloc(8);
    strcpy(g, "GGGGGGGG");
    fprintf(stderr, "这一次 malloc(8) 就申请到了栈上去: %p\n", g);
}

程序开头先定义了一个stack_var的数据,是为了方便在栈上伪造堆块,mallo的时候将堆块分配到栈上,我们在main函数处下断点,单步调试,
首先malloc三个堆块,然后free chunk a两次,这样就造成了double free 可以看到chunk a和chunk b形成了一个循环链表
在这里插入图片描述
在这里插入图片描述
在这个时候我们再malloc两次,然后将chunk a和chunk b分配出来,这时候fastbin链中还剩下一个chunk 其实这个chunk就是chunk a。因为它和前面分配出去的chunk d是同一个chunk,所以我们可以通过修改chunk d来修改fast bin中剩余的一个chunk,我们将fast bin中的chunk 的fd指针修改为我们伪造的一个空间地址,这样就就能够伪造一个chunk
下面是再次mallo两次过后的情况,这时fastbin中其实只剩一个chunk,只是chunk a中的数据还未置0,所以看到的是chunk a指向chunk b
在这里插入图片描述
在伪造chunk时 会进行size位的检查,所以我们必须要在伪造的chunk的size位填上正确的大小,我们这里将其赋值为0x20,也就是刚才stack_var的位置就是size的位置,然后我们在指针d的位置写入stack_var-8的地址(也就是在fastbin中free的chunk的fd位置写入伪造的堆块地址,我们再次申请的时候就会把栈上的空间分配给程序)
然后再malloc 两次堆块,第一次会将原chunk a申请出来,第二次就会将伪造的堆块分配出来
下面的图可以看到已经成功伪造堆块了
在这里插入图片描述

4.fastbin_dup_consolidate(double free large bin)

这是一个通过malloc_consolidate的分配机制来绕过double free检查的机制,我们首先free一个chunk到fast bin中,当我们再次分配一个大的chunk,在分配大的chunk时,glibc会首先根据分配chunk的大小在bins中查找相应的chunk,如果没有相应大小的chunk,如果有就会直接返回这一个地址,如果没有找到,这时就会触发malloc_consoledate()机制,这是就会遍历fastbin链中的所有free chunk,然后将他们合并放入unsorted bin中,unsorted bin中的chunk会按照大小放入相应得bins中。这时我们就可以再次free ,形成double free。
首先malloc两个chunk,
在这里插入图片描述
然后free一个chunk,释放到fastbin中去
在这里插入图片描述
然后再申请一个大的chunk ,这时就会触发malloc_consolidate,将fastbin中的chunk合并然后放到unsorted bin中去,这是fastbin中为空,
在这里插入图片描述
fast bin(合并)—>unsorted bin->small bin ,最后chunk出现在small bin中,这时我们就可以double free了
在这里插入图片描述
最后再次malloc两次,就会malloc到同一个chunk(分别是fast bin和small bin中的chunk),也就是p4和p5是同一个chunk
在这里插入图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值