首页
社区
课程
招聘
[翻译]Malloc Maleficarum之The House of Prime
发表于: 2017-3-7 20:24 6206

[翻译]Malloc Maleficarum之The House of Prime

2017-3-7 20:24
6206

2001年底,"Vudo Malloc Tricks""Once Upon A free()"定义了Linux中的动态内存块溢出的开发。在2004年底,一系列关于GNU libc malloc的补丁实现了十多个强制性完整性断言,有效地地说明了现有的技术已经过时了。

正是由于这个原因,一个几乎不可能的小的建议,就是我打算展示Malloc Maleficarum

      The House of Prime

      The House of Mind

      The House of Force

      The House of Lore

      The House of Spirit

      The House of Chaos

The House of Prime

首先介绍The House of Prime。一个艺术家有一幅画的第一次描边,一个作家总有过写出一首诗的第一行的经历。而对于我来说,在堆溢出发面,则是House of Prime。这是第一次突破,同时也表明了接下来一切都会水到渠成。可以说,这就是拒绝不可能,同时这也是相当困难的。由于这些原因,我觉得有必要将Prime放在Malloc Maleficarum的首要位置,而且,这也是它应得的。

      从纯粹的技术角度出发,House of Prime或许是收集中最没有用的了。当条件允许的时候,使用House of  Mind 或者House of Sprit 几乎可以说总是更好的。为了成功地使用House of Prime技术,我们必须释放两个由自己控制的不同size字段的chunk,然后触发对malloc的调用。

      该技术大致的思想是在某些不可控的情况下(下面讨论),破坏fastbin的最大值,以便于允许我们劫持由malloc调用的时所使用参数的数据结构arena。这样就允许返回任意内存块,或者直接修改可执行的控制数据。

      正如前面所说的,这个技术首先调用free函数释放由我们自己控制的内存。调用free函数会继续调用public_fREe,进而调用内部的函数_int_free。对于House Of Prime来说,public_fREe的细节相对来说不重要。因此我们只需要关注_int_free即可。这是它在glibc-2.3.5中的代码:

  然后就会出现了一个大肆吹嘘的完整性测试。__builtin_expect被构造用于优化,并且不影响结果的正确性。我们必须确保两个测试都失败以便于继续执行。在这个阶段,达到这样的效果并不是很难。

      注意,我们并没有控制p的值。因此可以假设对于非对齐的测试将会失败。另一方面,我们控制了size的值。事实上,这是我们所拥有的最重要的控制,但其范围受到了限制。对于House of Primesize的确切的上界限制并不重要。下界的限制才是这一技术正确执行的关键。


其中PREV_INUSE, IS_MMAPPED 以及 NON_MAIN_ARENA 的定义对应着一个申请的块中size entry里最没有意义的三个比特。Chunksize宏用于清除这三个比特位,这就意味着我们可以控制的chunk最小的值就是8。我们继续看_int_free,然后就清楚为什么这很重要了。

这就是fastbin的代码了。至于什么是fastbin以及为什么它们会被使用已经超出了本文的范围了。但是请记住,House of Prime的第一步就是覆盖fastbin的最大sizeav->max_fast。为了达到这样的效果,从上面可以看出,我们必须首先提供具有较低下限大小的块。考虑到av->max_fast的默认值是72,我们就很容易知道fastbin代码主要是对于不大于这样大小的块进行操作。但是,也正是因为这个才会导致av-max_fast的损坏不会立即出现效果。

我们应该注意到avarena的指针,这个东西是一个控制结构,包含了fastbin的最大值,指向fastbins的指针数组。而且,av->max_fastav->fastbins是连续的。

假设下一个大小完整性检验失败了。那么fb就会被设置为&(av->fastbins[fastbin_index(size)]),而其中的size是我们给定的。最后计算出来的下标是由0开始的。然而,下标0对应的chunk大小为最小大小16(一个最小的malloc的块包含prev_sizesize)。因此,当我们提供下限值8的时候,什么会发生呢?我们需要进一步分析fastbin_index()

我们可以看出,如果原来是8的话,经过宏运算就会得到-1。因此,fb就会被设置为av->fastbins[-1]的地址。由于av->max_fast后续跟着av->fastbins相邻,因此很明显,fb被设置为了&av->max_fast

进一步,由于这时fb不指向p,第二个完整性测试也会失败,最后两行的代码就会被执行。故而,我们的chunk pforward指针被设置为av->max_fastav->max_fast被设置为p的值。

上面我们做了一个假设,那就是下一个大小的完整性检验失败。现实中,我们通常需要做一些工作才可以让它们一起失败。如果溢出可以写空字节,那我们就可以很容易达到目的了。然而,如果溢出以一个空字节结尾,那么我们的解法就会依赖于特定的应用。如果测试是因为溢出时自然的内存布局,那我们就不需要依赖于一些固定的条件了,这就没有问题了。否则,我们就需要做一些内存布局的操作来确保nextsize的值是由我们控制的。

然而,House of Prime的挑战部分并不是去覆盖av->max_fast,而是如何利用覆盖进行任意代码执行。House of Prime通过覆盖特定线程变量arena_key来做到这一点。这就是House of Prime所需要的最难的条件。首先,arena_key仅仅在glibc中的malloc函数使用USE_ARENAS标志(默认使用)被编译的时候才存在。进一步,最重要的是,arena_key的地址一定会比真实的arena地址高。

由于arena结构和arena_key来自不同的源文件,这种情况发生与否取决于目标libc是如何编译和链接的。我看到过两种不同的形式,因此,这一点很重要。对于现在,我们假设arena_key在高地址上,因此可以被fastbin 代码覆盖。

      Arena_key是线程特殊的数据,也就意味着每一个执行的线程都有自己独立的arena_key,不受其他线程的影响。因此,在将House of  Prime技术应用到一个线程程序的时候,我们可以考虑一下,否则arena_key可以安全地被作为正常数据去处理。

      Arena_key是一个很有意思的目标,因为它被宏arena_get使用来找到正在执行的线程。也就是说,如果arena_key被某个线程控制,并且areba_get被调用了,那么arena就可以被劫持了。这种类型的arena劫持接下来很快就会讲,但是在讲之前我们必须先来考虑一下实际重写arena_key

      为了覆盖arena_keyfastbin代码又会被利用。这对应着我们第二次调用free来释放我们可以控制大小的块。这在House of Prime的先决条件中已经列出来了。正常情况下,fastbin代码不能够写超过av->fastbin,但是既然av->max_fast在先前已经被破坏了,任何一个大小小于我们第一次使用的块的地址的chunk都会被这个fastbin 代码处理。因此,我们可以写到av->fastbins[fastbin_index(av->max_fast)],这样就有很大的范围来得到arena_key了。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 6112
活跃值: (1212)
能力值: (RANK:30 )
在线值:
发帖
回帖
粉丝
2
辛苦辛苦
2017-3-8 10:01
0
雪    币: 97697
活跃值: (200829)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
3
поддержка!
2017-3-9 15:32
0
游客
登录 | 注册 方可回帖
返回
//