largebin-attack

对于largebin attach的学习,这段时间决定好好复习一下基础。PWN还是很吃基础的。heap还有一些攻击方法,后续也会接着学习。

largebin attack

2.29的largebin attack可以说是unsortedbin attack的替代品。在2.29之前,unsortedbin attack的作用是能够在任意地址写入一个main_arena地址(可控的堆地址)。可以用来控制循环,或者伪造vtable打fsop。后者是在只能控制unsortedbin时一种很常用的一种攻击手法。然而由于2.29的保护措施,unsortedbin attack几乎不能使用了。

largebin attack原理

攻击产生的原因是在malloc时,如果在unsortedbin中没有找到可以切割的块,就会把他们按照各自的大小,放到smallbin和largebin中。在其中缺少对largebin跳表指针的检测。以下是入bin操作的源码。

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
39
40
41
42
43
44
45
46
47
48
49
50
51
else
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;

/* maintain large bins in sorted order */
if (fwd != bck)
{
/* 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))
{
fwd = bck;
bck = bck->bk;

victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; // here1
}
else
{
assert (chunk_main_arena (fwd));
while ((unsigned long) size < chunksize_nomask (fwd))
{
fwd = fwd->fd_nextsize;
assert (chunk_main_arena (fwd));
}

if ((unsigned long) size
== (unsigned long) chunksize_nomask (fwd))
/* Always insert in the second position. */
fwd = fwd->fd;
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim; // here2
}
bck = fwd->bk;
if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
}
}

注意看注释写的here2部分。这里能够像unsortedbin一样,往可控块写入当前堆地址。

1
2
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;

largebin中 小于链表中最小的 chunk 的时候会执行前一句,反之执行后一句。当两者大小相同时,无法利用。

另外需要注意,伪造的堆块fd_nextsize需要设置为0.不然无法通过以下unlink检查。

1
2
3
4
5
6
7
8
9
size = chunksize (victim);

/* We know the first chunk in this bin is big enough to use. */
assert ((unsigned long) (size) >= (unsigned long) (nb));

remainder_size = size - nb;

/* unlink */
unlink_chunk (av, victim);

how2heap-largebin attack

现在的how2heap竟然有网页版了,方便很多

glibc2.27

首先程序分配了一个0x420的堆块,和一个0x20的保护堆块

1
2
unsigned long *p1 = malloc(0x420); // chunk1
malloc(0x20);

接下来分配了一个新的0x500的堆块。和一个保护堆块,之后再次重复。

1
2
3
4
unsigned long *p2 = malloc(0x500); // chunk2
malloc(0x20);
unsigned long *p2 = malloc(0x500); // chunk3
malloc(0x20);

之后free(p1),free(p2),他们会被放在unsortedbin中。

image-20220129142721399

接下来**malloc(0x90)**这将会从unsortedbin末尾chunk中切割出0xa0大小空间,并把0x510的chunk放到largebin中,然后剩下的0x430-0xa0的chunk,仍然被放回到unsortedbin中。这一步完成了许多过程

  • 从 unsorted bin 中拿出最后一个 chunk(p1 属于 small bin 的范围)
  • 把这个 chunk 放入 small bin 中,并标记这个 small bin 有空闲的 chunk
  • 再从 unsorted bin 中拿出最后一个 chunk(p2 属于 large bin 的范围)
  • 把这个 chunk 放入 large bin 中,并标记这个 large bin 有空闲的 chunk
  • 现在 unsorted bin 为空,从 small bin (p1)中分配一个小的 chunk 满足请求 0x90,并把剩下的 chunk(0x330 - 0xa0)放入 unsorted bin 中

如下图。

image-20220129143135813

接下来释放chunk3。他会被插入到unsortedbin中。可以看到是插在最前面。

image-20220129143454363

接下来是触发部分。设置p2(在largebins中的块)先看一下没改的时候堆布局

image-20220129144212899

将largebin按照如下方式修改。改自身size,bk和bk_nextsize。fd和fd_nextsize均写为0,按照前面所说的。

image-20220129144900563

在接下来分配0x90的块,使得unsortedbin中的chunk能够插入largebin之前,先看一下堆布局。

image-20220129145052204

之后,就能成功写入两个值。写入的是被插入unsortedbin中的chunk地址。

这是因为当前插入的chunk属于largebin,大小为0x410(取出Unsortedbin,放入对应largebin中)寻找到fwd的size是0x3f0被我们修改过,小于0x410,会相应的执行这一句

1
2
3
victim->bk_nextsize = fwd->bk_nextsize
// then
victim->bk_nextsize->fd_nextsize = victim;

注意这里的fwd是经过遍历比较找到的正好比自己小的那个chunk。这里0x3f0正好比自己小。插入在0x3f0后面。此时执行上述代码,相当于利用原先largebin中我们伪造好的指针,写入了main arena数据此时应该修改了var2,因为它在bk_nextsize位置。

image-20220129145322368

同时,可以看到var1也被修改了,因为存在以下利用。

1
2
3
4
5
bck->fd = victim;
// 等价于
(fwd->bk)->fd = victim;
// 等价于
*(addr1+2) = victim;

相当于是这个chunk入链的操作,这里和nusortedbin attack一模一样。

总结-2.27

利用largebin attack的条件,和能够达成的效果。

条件:

可以修改一个 large bin chunk 的 data

  1. victim chunk’s size修改成比下一个进来的chunk小即可,但是要确保被修改的首先被存放在largebins中,因为要从 unsorted bin 中来的 large bin chunk 要紧跟在被构造过的 chunk 的后面
  2. fd_nextsize修改为0
  3. 修改fd_nextsize为target-0x20,fd为target-0x10,就可以在这两个地方写入数据

效果:

  1. 通过 large bin attack 可以辅助 Tcache Stash Unlink+ 攻击

  2. 可以修改 _IO_list_all 便于伪造 _IO_FILE 结构体进行 FSOP

注意:

unsortedbin插入在头部,取出在尾部

largebin中

  • 按照大小从大到小排序
  • 若大小相同,按照free时间排序
  • 若干个大小相同的堆块,只有首堆块的fd_nextsizebk_nextsize会指向其他堆块,后面的堆块的fd_nextsizebk_nextsize均为0
  • size最大的chunk的bk_nextsize指向最小的chunk; size最小的chunk的fd_nextsize指向最大的chunk

image-20220129163143279

glibc2.31

glibc2.31为largebin添加了两条类似unsortedbin中的检查。分别检查bck和bk指针是否被修改过。

1
2
3
4
5
6
7
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}

绕过此检查的基本思路是:分配一个最小的chunk到largebin中,如果当前块比这个bin中所有块都要小,就能够绕过检验,直接写入。注意上面是改的较小的chunk,这里是较大的。然后最小的chunk

接下来是how2heap的做法

首先是创建了0x428,0x18,0x418,0x18四个chunk。记0x428的chunk为p1,0x418的chunk为p2。大小选择需要确保p2小于p1,并且两个在同一个bins中。

接着释放p1(较大的那个),p1将存在于unsortedbin中,分配一个p3(比p1大)让p1进入largebin。现在bins如下所示。

image-20220129161319249

之后释放p2,p2进入unsortedbin。如下图

image-20220129161549534

现在修改p1的bk_nextsize为我们想要的地址-0x20

image-20220129161858696

之后创建一个chunk,使得unsortedbin中的chunk(p2)被放入largebin中。注意,此时p2大小是小于p1的。

此时glibc有一个机制:当被放入的chunk是当前largebin中最小的chunk时,不会检查 chunk->bk_nextsize。我们上述修改就会生效。

此时,目标地址将写入原先在unsortedbin中的chunk的地址。

image-20220129162221450

这里修改的又是较大的chunk,可能会感到疑惑?

这是因为largebin 的fd_nextsize和bk_nextsize构成的是一个环形链表。可能需要记住的是在2.31中,修改的是较大块,在2.27之前,修改的是较小的

参考链接

https://www.anquanke.com/post/id/189848

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/large-bin-attack/

https://www.anquanke.com/post/id/242640

https://xz.aliyun.com/t/5177?page=1

文章目录
  1. 1. largebin attack
    1. 1.1. largebin attack原理
    2. 1.2. how2heap-largebin attack
      1. 1.2.1. glibc2.27
      2. 1.2.2. 总结-2.27
      3. 1.2.3. glibc2.31
    3. 1.3. 参考链接
|