Source code demonstrates the use-after-free
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int main() 6 { 7 fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n"); 8 fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n"); 9 fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n"); 10 fprintf(stderr, "This can be exploited in a use-after-free situation.\n"); 11 12 fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n"); 13 char* a = malloc(512); 14 char* b = malloc(256); 15 char* c; 16 17 fprintf(stderr, "1st malloc(512): %p\n", a); 18 fprintf(stderr, "2nd malloc(256): %p\n", b); 19 fprintf(stderr, "we could continue mallocing here...\n"); 20 fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n"); 21 strcpy(a, "this is A!"); 22 fprintf(stderr, "first allocation %p points to %s\n", a, a); 23 24 fprintf(stderr, "Freeing the first one...\n"); 25 free(a); 26 27 fprintf(stderr, "We don't need to free anything again. As long as we allocate less than 512, it will end up at %p\n", a); 28 29 fprintf(stderr, "So, let's allocate 500 bytes\n"); 30 c = malloc(500); 31 fprintf(stderr, "3rd malloc(500): %p\n", c); 32 fprintf(stderr, "And put a different string here, \"this is C!\"\n"); 33 strcpy(c, "this is C!"); 34 fprintf(stderr, "3rd allocation %p points to %s\n", c, c); 35 fprintf(stderr, "first allocation %p points to %s\n", a, a); 36 fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n"); 37 }
operation result
checksec
A first application of a 512-byte memory
Then apply a piece of 256 bytes of memory b
At this time, the distribution of the heap
0x211 prev_inuse is shown here because the bit size field 512 = 0x200 0x10 byte fixed header plus stack plus the head portion is 1 i.e. 0x200 + 0x10 + 1 = 0x211
Similarly 0x111 = 0x100 + 0x10 + 1 = 0x111
32 the stack structure 64 are all 8 bytes in each field is 4 bytes
1 struct malloc_chunk { 2 3 INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ 4 INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ 5 6 struct malloc_chunk* fd; /* double links -- used only if free. */ 7 struct malloc_chunk* bk; 8 9 /* Only used for large blocks: pointer to next larger size. */ 10 struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ 11 struct malloc_chunk* bk_nextsize; 12 };
这里要注意两块都大于0x70字节,即大于fastbin的最大size,否则释放后进入fastbin链表(linux中的快表)
这里释放后进入unsorted bin链表
在第一块512字节的内存中存放字符串“this is A”
存放字符串之前a内存
prev_size=0 size=0x211 数据部分为空
存放字符串之后a内存
然后释放内存a,进入unsorted bin链表
之后申请500字节的内存c
由于linux堆的分配机制,先到unsorted bin链表中寻找,从释放的a内存512字节中切割出500字节给c
所以c内存和a内存指向的地址相同
然后向c内存中写入字符串“this is C”
查看此时堆内存
可以看到和a内存相比,0x603018处字节由0x41(A的ascii码)变成了0x43(C的ascii码)
要注意的是,linux堆管理机制规定堆块的大小必须是 2 * SIZE_SZ 的整数倍。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中,SIZE_SZ 是 4;64 位系统中,SIZE_SZ 是 8
所以这里500+16=516字节被转换成了0x210=528字节 即a内存和c内存重合
此时a和c指向同一块内存,如果此后再显示a的内容,即输出了c的内容
这种情况即造成了use-after-free
通过这个例子,可以很清楚的了解linux下堆的分配机制和堆的结构。