双重自由攻击的理解

understanding of double free attack

我从这里得到了一小段代码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,为什么要两次返回同一个块?

谢谢!

这是特定于编译器的 "exploit"。首先,让我们叫出房间里的大象:

  1. 您是故意使用未定义行为 (UB),因此从那时起,所有赌注都将取消。
  2. 您没有使用 cppcheck or lint, nor any debugging tools like valgrind 等任何静态代码分析工具来捕捉此信息。在生产系统中,您将使用此类工具至少尝试捕获这些错误。
  3. 您可以随时获取最新的 glibc source 并自己发现它:)

现在,进入正题。首先,此漏洞利用仅在启用 "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 (since glibc 2.3) Set the upper limit for memory allocation requests that are satisfied using "fastbins". (The measurement unit for this parameter is bytes.) Fastbins are storage areas that hold deallocated blocks of memory of the same size without merging adjacent free blocks. Subsequent reallocation of blocks of the same size can be handled very quickly by allocating from the fastbin, although memory fragmentation and the overall memory footprint of the program can increase. The default value for this parameter is 64*sizeof(size_t)/4 (i.e., 64 on 32-bit architectures). The range for this parameter is 0 to 80*sizeof(size_t)/4. Setting M_MXFAST to 0 disables the use of fastbins.

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

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

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

$ cppcheck --enable=all test.cpp 
  Checking test.cpp...
  [test.cpp:27]: (error) Memory pointed to by 'a' is freed twice.
  [test.cpp:33]: (error) Memory leak: c
  [test.cpp:30]: (error) Allocation with malloc, printf doesn't release it.
  [test.cpp:27]: (error) Deallocating a deallocated pointer: a

前面的回答其实并没有解释清楚。这里:

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

因为 glibc 实现包含基本检查以确保将块添加到空闲列表时,它不在该列表中(您最终得到一个自引用指针)。这不是一种安全防御,而是一种针对明显错误的幸运保护

为什么我释放B后又可以释放A?

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

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

所以不会有任何自引用指针

如果我再次 malloc,为什么要两次返回同一个块?

当您请求第一个块时,您会得到 A,然后是 B,然后是 A。它只是 "popping" 空闲列表中的下一个项目。