largebin structure & attack methods

发布于 2020-07-28  2593 次阅读


structure

large_bin 与 small_bin 相似,却又不完全相同。

一言以蔽之,large_bin 继承了 small_bin 的双链表结构,但在其基础上添加了一个双链表,用于快速查找堆块。

同一个 large_bin 中存放的并不是单一 size 的 chunk,而是在某一范围的chunk,详情可见下表:

size range

size    index
[0x400 , 0x440) 64
[0x440 , 0x480) 65
[0x480 , 0x4C0) 66
[0x4C0 , 0x500) 67
[0x500 , 0x540) 68
等差 0x40 …
[0xC00 , 0xC40) 96
[0xC40 , 0xE00) 97
[0xE00 , 0x1000)    98
[0x1000 , 0x1200)   99
[0x1200 , 0x1400)   100
[0x1400 , 0x1600)   101
等差 0x200    …
[0x2800 , 0x2A00)   111
[0x2A00 , 0x3000)   112
[0x3000 , 0x4000)   113
[0x4000 , 0x5000)   114
等差 0x1000   …
[0x9000 , 0xA000)   119
[0xA000 , 0x10000)  120
[0x10000 , 0x18000) 121
[0x18000 , 0x20000) 122
[0x20000 , 0x28000) 123
[0x28000 , 0x40000) 124
[0x40000 , 0x80000) 125
[0x80000 , …. ) 126

largebin管理的是一个范围区间的堆块,此时fd_nextsizebk_nextsize就派上了用场。

example

现在假设我们有5个 large_chunk:0x420,0x420,0x410,0x400,0x400

他们都属于同一个 largebin 我们来看看它们在这个 bin 中的排布:

可以看到图中有蓝色红色两种线。

红色就是我们 small_bin 中的双向链表指针,不必多说。

蓝色 是 largebin 中引入的一种新指针,它负责把不同 size 的 堆头(如果有两个相同 size 的 chunk,那么先进入 large_bin 的作为堆头,其他的 chunk 都只能接在其后)。所有堆头通过 bk_nextsizefd_nextsize 连接在一起,能够缩短寻找 chunk 的时间。

用结构体表示大概长这样:

struct large_chunk{
    size_t prev_size;
    size_t size;
    (void*) fd;
    (void*) bk;
    (void*) fd_nextsize;
    (void*) bk_nextsize;
}

unlink

https://mrh1s.top/archives/619#toc-head-9

unlink 有对于 fd、bk、fd_nextsize、bk_nextsize 的检测。当然,如果 large_chunk 不是堆头(即 fd_nextsize == bk_nextsize),则不会检测后两项。

malloc

https://mrh1s.top/archives/619#toc-head-14

首先会去 large_bin 中进行搜寻合适大小的 large_chunk,若没有合适目标,便会去 unsorted bin 进行寻找,最后会直接从 top_chunk 切割。

free

https://mrh1s.top/archives/619#toc-head-25

和 smallbin 的操作相同,会将 chunk 直接放入 unsorted bin,后续再放入各 large_bin 中,这个操作用到了 unlink 宏。

chunk 放入 large_bin 的方式如下:

  1. 如果堆头(相同 size 的 chunk 的头部)不存在,那么就作为堆头;
  2. 如果堆头存在,那么始终将 chunk 放在堆头之后,即 FILO 原则。

attack

malloc fake_large_chunk

这个方法与 smallbin 进行 unlink 从而控制堆块之外的空间有异曲同工之妙。

image.png

首先我们申请一定的空间(大小合适即可,要求不高),在其中伪造 fake_chunk。

fd_nextsizebk_nextsize 直接置零,这样可以避免 unlink 的 check。

然后,mallocfree 掉一个 large_chunk,这个 large_chunk 的大小应该大于刚刚伪造的 fake_chunk,记为 real_chunk

用某种方法触发 unsorted bin 的 chunk 回收机制,使 large_chunk 掉入相应的 bin

接下来利用 UAF 漏洞修改 real_chunk->bk_nextsizefake_chunk_addr

malloc(fake_chunk->size-0x10) 即可申请到该 fake_chunk

largebin attack

与 unsorted bin attack 类似,通过伪造指针,可以修改任意地址为当前堆块。

以下源码是 _int_malloc 执行时,将 large_chunk 放入 largebin 的部分,victim 被放入了两个双向链表中。

          ...//将largebin从unsorted bin中取下
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);

          ...

                         //否则这个chunk将会成为堆头,<code>bk_nextsize</code>和<code>fd_nextsize</code>将被置位
                          victim->fd_nextsize = fwd;
                          victim->bk_nextsize = fwd->bk_nextsize; //由于fwd->bk_nextsize可控,因此victim->bk_nextsize可控
                          fwd->bk_nextsize = victim;
                          victim->bk_nextsize->fd_nextsize = victim; //victim->bk_nextsize可控,因此实现了往任意地址写victim的能力
                        }
                      bck = fwd->bk; //由于fwd->bk可控,因此bck可控
                 ...

          mark_bin (av, victim_index);
          //设置fd与bk完成插入
          victim->bk = bck; 
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim; //bck可控,因此实现了往任意地址写victim的能力
          ...
        }

注意以下两个语句,victim->bk_nextsize 全取自 fwd->bk_nextsize,且未作任何检查,那么将 fwd->bk_nextsize修改为 &target - 0x20,就可以在 target 处写 victim 的地址

victim->bk_nextsize = fwd->bk_nextsize;
victim->bk_nextsize->fd_nextsize = victim;

同样,后面的链表也未进行检验操作,同样将 fwd->bk 修改为 &target - 0x10,便可以在 target 处写 victim 的地址

victim->bk = bck; 
bck->fd = victim;

CTFer|NOIPer|CSGO|摸鱼|菜鸡