了解双重免费攻击

时间:2016-04-04 14:53:35

标签: c linux heap glibc

我从这里获得了一小段代码https://github.com/shellphish/how2heap/blob/master/fastbin_dup.c

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

int main()
{
    printf("This file demonstrates a simple double-free attack with fastbins.\n");

    printf("Allocating 3 buffers.\n");
    int *a = malloc(8);
    int *b = malloc(8);
    int *c = malloc(8);

    printf("1st malloc(8): %p\n", a);
    printf("2nd malloc(8): %p\n", b);
    printf("3rd malloc(8): %p\n", c);

    printf("Freeing the first one...\n");
    free(a);

    printf("If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
    // free(a);

    printf("So, instead, we'll free %p.\n", b);
    free(b);

    printf("Now, we can free %p again, since it's not the head of the free list.\n", a);
    free(a);

    printf("Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
    printf("1st malloc(8): %p\n", malloc(8));
    printf("2nd malloc(8): %p\n", malloc(8));
    printf("3rd malloc(8): %p\n", malloc(8));
}
  1. 为什么我不能连续两次释放A?
  2. 为什么我可以在释放B后再次释放A?
  3. 如果我再次使用malloc,为什么还要给我两次相同的块?
  4. 谢谢!

3 个答案:

答案 0 :(得分:1)

这是一个特定于编译器的“漏洞利用”。首先,让我们在房间里叫出大象:

  1. 你故意使用未定义的行为(UB),所以从那时起,所有的赌注都会被取消。
  2. 您没有使用任何静态代码分析工具(如cppcheck或lint),也没有使用任何调试工具(如valgrind)来捕捉这一点。在生产系统中,您将使用此类工具至少尝试捕获这些错误。
  3. 您可以随时提取最新glibc source并自行发现:)
  4. 现在,问题。首先,这个漏洞只适用于启用了“fastbins”的GCC。如果您只是在代码中添加以下内容:

    #include <malloc.h>
    // ...
    mallopt(M_MXFAST, 0);
    

    然后它会更快崩溃:

    This file demonstrates a simple double-free attack with fastbins.
    Allocating 3 buffers.
    1st malloc(8): 0x556f373b1010
    2nd malloc(8): 0x556f373b1030
    3rd malloc(8): 0x556f373b1050
    Freeing the first one...
    If we free 0x556f373b1010 again, things will crash because 0x556f373b1010 is at the top of the free list.
    So, instead, we'll free 0x556f373b1030.
    Now, we can free 0x556f373b1010 again, since it's not the head of the free list.
    *** Error in `./a.out': double free or corruption (!prev): 0x0000556f373b1010 ***
    Aborted (core dumped)
    

    这是由于“fastbins”的工作原理:

      

    M_MXFAST(自glibc 2.3起)                 设置内存分配请求的上限                 满意使用“fastbins”。 (测量单位                 参数是字节。)Fastbins是保存的存储区域                 释放相同大小的内存块而不合并                 相邻的自由街区。随后重新分配块                 通过分配,可以非常快速地处理相同的大小                 fastbin,虽然内存碎片和整体                 程序的内存占用可以增加。默认                 该参数的值是64 * sizeof(size_t)/ 4(即64开                 32位架构)。此参数的范围是0到                 80 *的sizeof(为size_t)/ 4。将M_MXFAST设置为0将禁用该功能                 fastbins。

    免费通话不会立即释放内存,只是将其标记为可用于将来的malloc()通话。如果你立即尝试连续两次针对相同的内存块发出free()调用,内部指针检查将捕获它,但损坏已经完成(UB调用),但同样的检查赢了“处理你提出的例子。

    至于最后3次malloc()次调用生成相同地址两次:UB已被调用,并且已损坏空闲列表。

答案 1 :(得分:1)

先前的答案实际上并没有解释。在这里:

为什么我不能连续两次释放A?

因为glibc实现包含基本检查,以确保将块添加到空闲列表时,该列表中还没有该块(您最终获得了自引用指针)。这样的安全防御并不是一种安全防御,而是一种幸运的针对明显漏洞的保护措施

为什么在释放B后又能释放A?

因为自由列表中的A不会与A相邻。空闲列表如下:

-->A-->B-->A

因此不会有任何自引用指针

如果再次分配内存,为什么要让我两次回到同一块?

当您请求第一个块时,您会得到A,然后是B,然后是A。它只是从空闲列表中“弹出”下一个项目。

答案 2 :(得分:0)

根据DevNull的建议,静态代码分析可以帮助您检测潜在的编码问题。以下是扫描代码时cppcheck的输出:

initial begin
   forever begin    
      @(posedge reset); // wait for the rising edge of reset

      // reset has occured at this time.
      @(posedge clk); // delay for 1 clock cycle

      // check your data here
      if(data == 0) $display("ok");
      else $display("error");
   end
end