翻译:lufei
预估稿费:260RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
传送门:【技术分享】Windows Exploit开发系列教程——堆喷射(一)
前言
大家好,欢迎回到本部分教程堆喷射2部分。本教程将引导您在IE8上使用精密堆喷射。
有两种基本的场景下,需要你使用非常精确的堆喷射:
(1)你必须处理DEP防护情况下,你需要将执行流程从你的ROP链开始。
(2)你利用Use-After-Free,需要满足虚函数的一些数据处理流程。
我想找到一个处理这两个问题的例子,但是许多这样的漏洞是一个相当复杂,不一定适合作为教程。
应该明白两个道理。首先,实践动手才是最好,找到漏洞,把它们的难点分开,解决一些难点,尝试更难的,再减少难点,循环下去继续。其次,本教程不关注漏洞分析,因为这些教程是关于在编写漏洞攻击和如何克服它们时你将面临的障碍。
今天我们来看看MS13-009这个漏洞,你可以在这里找到metasploit模块。 如果你想更好地掌握本章的教程内容,我强烈推荐下面添加的一些链接阅读材料。
调试机器:
Windows XP SP3 with IE8
链接:
Exploit writing tutorial part 11 : Heap Spraying Demystified (corelan) – here
Heap Feng Shui in JavaScript (Alexander Sotirov) – here
Post-mortem Analysis of a Use-After-Free Vulnerability (Exploit-Monday) – here
Heap spraying in Internet Explorer with rop nops (GreyHatHacker) – here
CVE-2013-0025 MS13-009 IE SLayouRun (Chinese analysis of ms13-009, you will probably need to load this from the google cache) – here
介绍
我想这个课题需要一些介绍,但你会发现,许多障碍,对你不陌生。我不会深入到所有更细微的点,因为这将需要很多时间。如果这里的一些技术不熟悉,我建议你阅读本教程系列的第7部分(面向返回编程)和第8部分(堆喷射[第1章:可控EIP])。
我们谈论Use-After-Free时,需要了解什么是虚表。C++语言允许基类定义虚函数。基类派生类也可以定义自己的函数(与虚函数同名)。因此,虚拟函数允许派生类替换基类函数。编译器会确保每当调用的对象实际上是派生类时,总是调用替换。所有这一切发生在运行时。虚表包含指向基类中定义函数的指针。当需要在运行时调用函数时,根据需要它的派生类从虚表中选择适当的指针。我们可以看到下面的图形表示。
1.1
Use-After-Free漏洞通常相当复杂,其原因因案例而异。通常执行流程的工作原理是这样的:
(1)在某个时刻一个对象被创建并与一个vtable相关联;
(2)该对象被一个vtable指针调用。如果我们释放对象在它被调用之前,程序将崩溃,当它后来试图调用对象(例如:它尝试使用对象后它被释放 – UAF)。
为了利用这个问题,我们将一般地执行以下步骤:
(1)在某个点创建一个对象;
(2)我们对这个对象触发后释放;
(3)创建我们自己的对象,对象大小尽可能与上次创建的对象大小接近;
(4)以后当vtable指针被调用时,我们自己创建的假对象将被使用,我们获得代码执行。
这听起来非常复杂,但通过实例演示将变成简单。首先,我们将创建一个可靠的堆喷射,然后我们将专注于ms13-009!
堆的Shellcode
正如我们在第8部分中所做的那样,我想从IE8上获得可靠的喷喷射开始。 继续我们之前做的工作,修改我们之前的POC。 这个POC已经从第8部分中的版本略有修改。这里的主要区别是我已经添加了一个alloc函数,它将我们的缓冲区作为输入,调整分配的大小,使它们匹配BSTR规范(我们需要 减去6以补偿BSTR头和尾,并除以2,因为我们使用unicode unescape)。
让我们用windbg调试器,看看当我们执行这个喷射时会发生什么。
下面的图像是我们的喷射表示。 我们已经填充了150mb的我们自己的数据,这150mb被分成150块1mb(每个块被存储为一个单独的BSTR对象)。 这个BSTR对象又填充了包含我们的shellcode和我们的NOP的0x1000十六进制(4096字节)的块。
1.2
到现在为止还挺好! 接下来,我们需要重新调整我们的堆喷射,以便shellcode变量完全指向0x0c0c0c0c,这将是我们的ROP链的开始。 考虑如果0x0c0c0c0c被分配在内存中的某个地方,因为我们的堆喷射,那么它必须有一个特定的偏移量在我们的0x1000块。 我们要做的是计算从块开始到0x0c0c0c0c的偏移,并将其作为填充添加到我们的喷射。
如果你重新运行上面的喷射,你会注意到0x0c0c0c0c不会总是指向相同的堆,但是从我们的0x1000十六进制块的开始到0x0c0c0c0c的偏移将始终保持不变。 我们已经拥有了计算填充大小所需的所有信息。
让我们修改POC并在调试器中重新运行喷射。
正如我们在下面可以看到的,我们已经设法将我们的shellcode重新对齐到0x0c0c0c0c。 事实上,当我们在内存中搜索字符串“FuzzySecurity”时,我们可以看到所有位置都在相同的字节结尾0x?????c0c。
所以我们现在用这样的方法调整我们的堆喷射,我们可以使我们的shellcode指向我们选择的任意地址(在这种情况下为0x0c0c0c0c)。 堆喷射在IE7-8上工作,并已在Windows XP和Windows 7上测试。通过一些修改,它可以在IE9上工作,但这不在本教程的范围。
详解MS13-009
如前所述,本教程的主要目标不是分析漏洞,而是要理解在编写exploit时您面临的障碍。 然而,我们将快速查看该漏洞,以了解发生了什么。 以下POC是触发错误的最精简的案例文件。
好,让我们看看调试器,看看当我们触发漏洞时会发生什么。 你会注意到我已经添加(但注释掉)CollectGarbage()函数。 在我的测试期间,我注意到poc不可靠(只有大约80%),所以我正在试验CollectGarbage(),看看是否会提高可靠性。 CollectGarbage()是javascript公开的一个函数,它清空了四个bin,这些bin通过oleaut32.dll中的自定义堆管理引擎实现。当我们尝试在堆上分配我们自己的假对象,将与之相关。 从我的测试,我不能确定它有什么区别,但如果任何人有任何想法,在下面留下评论。
从下面的执行流程我们可以看到一个对象试图调用vtable中与EAX偏移量为0x70十六进制的函数。
stacktrace向我们展示了导致崩溃的执行流程。 如果我们在返回地址(在那里调用应该返回)如果没有崩溃,我们可以看到我们的函数是如何调用的。 看起来像EBX中的一些对象通过它的vtable指针ECX,然后后来被mshtml!CElement :: Doc引用调用一个函数在0x70十六进制偏移量。
通过使用一些巧妙的断点,我们可以跟踪由mshtml!CTreeNode做出的分配,以查看是否有任何熟悉的值弹出。 下面的结果表明EBX指向CparaElement,并且应该被调用的函数是Celement :: SecurityContext。 这似乎与MS13-009的漏洞描述相一致:“Microsoft Internet Explorer中的Use-After-Free漏洞,其中释放了一个CParaElement节点,但仍在CDoc中保留引用。当CDoc重新布局时,此内存被重用 执行“。
MS13-009 EIP
正如我前面提到的,这里的主要重点是如何克服我们在exploit过程中遇到的障碍,所以我不会花时间来解释如何在堆上分配我们自己的对象。 相反,我将使用来自公开可用的漏洞的代码段。 我们的新POC可以在下面看到。
再次注意CollectGarbage()函数,随意使用它,看看它是否有任何重大差异,当尝试分配对象。 让我们看看调试器,看看当我们执行这个POC会发生什么。
如果0x0c0c0c7c是存储器中的有效位置,则此指令序列将最终调用0x0c0c0c7c(= EIP)的DWORD值,此时不是这种情况。 记住我们的堆喷射设置为将shellcode变量对齐到0x0c0c0c0c,我们将看到为什么这是必要的。 只要记住我们可以设置EIP为任何我们想要的值,例如0xaaaaaaaa的DWORD值。 这可以通过用0xaaaaaaaa-0x70 = 0xaaaaaa3a重写EAX来实现。 你可以看到下面的例子。
让我们来看看调试器,以验证我们现在将最终覆盖EIP与0xaaaaaaaa。
MS13-009 Code Execution
我们已经走了很远! 综合我们迄今为止所做的工作,我们可以开始我们的代码执行之旅。 第一个任务是创建我们的新POC,其中包含我们的喷射并触发漏洞。
从下面的截图我们可以看到,我们覆盖了EIP与0x90909090,这是因为EIP从位于0x0c0c0c0c + 0x70 = 0x0c0c0c7c的DWORD的值,它指向我们的nopslide。
1.3 EIP in NopSlide
这可能看起来有点混乱,希望下面的视图将帮助弄清过程!
让我们尝试填充我们的shellcode变量,以便我们可以精确地覆盖EIP。 我们可以通过在缓冲区长度为0x70十六进制(112字节= 28-DWORD)的前面添加我们的unescape ASCII字符串来实现。
如预期,我们现在可以完全控制EIP。 作为提醒,EIP中的值为小端。
1.4 EIP Override
我们的0x1000十六进制块的新布局如下。
好完美!现在我们要面对我们的下一个障碍。我们的ROP链和shellcode将位于堆上,但我们的堆栈指针(= ESP)指向mshtml内部。我们执行的任何ROP小部件都将返回堆栈中的下一个地址,因此我们需要将堆栈从mshtml转移到堆上控制的区域(我们的0x1000字节块)。因为你会记得EAX正好指向我们的shellcode变量的开头,所以如果我们找到一个ROP小部件将EAX移动到ESP或交换它们,我们将能够枢转堆栈并开始执行我们的ROP链在0x0c0c0c0c。
我将使用来自与java6一起打包的MSVCR71.dll的ROP小工具,并由Internet Explorer自动加载。我在下面包含了由mona生成的两个文本文件:
(1)MSVCR71_rop_suggestions.txt,其中包含一个主题化的ROP小工具列表;
(2)MSVCR71_rop.txt,它包含一个ROP小工具的原始列表;
如果你想使用他们,我建议你下载文件并使用正则表达式解析它们。
MSVCR71_rop_suggestions.txt – here
MSVCR71_rop.txt – here
解析文本文件,我们可以轻松地找到我们需要的小工具,让我们修改我们的POC,并验证一切正常工作。
从下面的屏幕截图我们可以看到,我们在XCHG EAX,ESP上下了断点,如果我们继续执行流程,我们成功地跳转到堆栈并尝试在0x0c0c0c0c执行第一个DWORD。
1.5 Breakpoint – Stack Pivot
1.6 EIP = 0x42424242
我们几乎解决所有的困难。 我们现在必须执行一个ROP链,用它禁用内存区域的DEP,因此我们可以执行第二阶段的有效负载。 幸运的是,MSVCR71.dll被攻击者反复滥用,并且已经存在一个由corelanc0d3r在这里创建的优化的ROP链。 让我们在我们的POC中插入这个ROP链并重新运行漏洞。
从屏幕截图中我们可以看到,我们在跳转堆栈后打开我们的第一个gadget,并且在我们调用VirtualProtect后,我们到达了剩下的垃圾。
1.7 ROP gadget at 0x0c0c0c0c
1.8 Leftover junk 0x41414141
现在剩下的就是在我们的垃圾缓冲区结束时插入一个跳转,跳过我们的初始EIP覆盖(XCHG EAX,ESP#RETN)。 在我们的短跳跃之后放置的任何shellcode将被执行
在执行我们的短跳后,我们可以自由执行我们选择的任何shellcode!
1.9 0xeb04 short jump Shellcode + Game Over
现在到了容易的部分,让我们生成一些shellcode!
好,现在让我们整理我们的POC,添加注释和运行最终的漏洞。 我想再次提及,这个漏洞有一些可靠性问题(只有正常触发的几率是80%),如果任何人有问题,请在下面留下评论。
2.0 Game Over