Fastbin attack之double free

学习一下double free

fastbin attack介绍

基于fastbin的攻击,fastbin在2.23中,2.27等版本利用都有些不同,但是都有前提:

  • 存在堆溢出、use-after-free等能控制chunk内容的漏洞
  • 漏洞发生于fastbin类型的chunk中

fastbin attack可以分:

  • Fastbin Double Free
  • House of Spirit
  • Alloc to Stack
  • Arbitrary Alloc

原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//gcc -g z1r01.c -o z1r01
#include <stdio.h>
int main(int argc, char **argv)
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x30);
chunk2=malloc(0x30);
chunk3=malloc(0x30);
//free
free(chunk1);
free(chunk2);
free(chunk3);
return 0;
}

直接b 10调试

看一下创建的4个堆吧

在第13下断点

释放后的chunk都指向前一个chunk的fd,回来在看释放后的内存情况,chunk的prev_inuse位依然还是1不变

double free

从名字上可以理解free两次,其实也可以多次,可以成功利用需要两个原因:

  • fastbin的堆块被释放后next_chunk的prev_inuse位不会被清空
  • fastbin在执行free的时候仅验证了main_arena直接指向的块,即链表指针头部的块。对于链表后面的块并没有进行验证
1
2
3
4
5
6
7
8
9
10
11
12
//gcc -g z1r02.c -o z1r02
#include<stdio.h>
int main()
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x10);
chunk2=malloc(0x10);

free(chunk1);
free(chunk1);
return 0;
}

这样子的double free会被检测出来

那变换一下在两个chunk1中间再加个chunk2

1
2
3
4
5
6
7
8
9
10
11
12
13
//gcc -g z1r03.c -o z1r03
#include<stdio.h>
int main()
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x10);
chunk2=malloc(0x10);

free(chunk1);
free(chunk2);
free(chunk1);
return 0;
}

直接b 12

可以看到chunk1的fd指向了chunk2,chunk2的fd指向了chunk1,之后以会这样是因为在free(chunk1);free(chunk2);之后,应该是chunk2fd指向chunk1,但是后面又来了一个free(chunk1);所以chunk1的fd指向了chunk2

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.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 *chunk1,*chunk2,*chunk3;
void *chunk_a,*chunk_b;

bss_chunk.size=0x21;
chunk1=malloc(0x10);
chunk2=malloc(0x10);

free(chunk1);
free(chunk2);
free(chunk1);

chunk_a=malloc(0x10);
*(long long *)chunk_a=&bss_chunk;
malloc(0x10);
malloc(0x10);
chunk_b=malloc(0x10);
printf("%p\n",chunk_b);
return 0;
}

第21行下个断点

继续25行下个断点,可以看到和上面的一样,chunk的fd都指向对方

n两下,两个行代码可以看成将chunk1的fd给变成了bss_chunk

再来一下n,申请了一个堆。将0x555555b020给申请了,因为是LIFO链表

再申请一个堆,就会将chunk1给申请,还有一个bss_chunk,假如再申请一个0x10大小的堆,就会被申请在bss_chunk这个地址

申请了一个0x10的堆,就会被申请在bss_chunk这里

最后输出bss_chunk地址