【技术分享】在ie8中利用ROP与Heap Spray利用反弹shell

阅读量    33420 |   稿费 260

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

https://p0.ssl.qhimg.com/t0110d5357a9ec3d6c1.png

翻译:Ox9A82

预估稿费:260RMB(不服你也来投稿啊!)

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

简介

这个漏洞利用程序是针对Windows 7平台上的IE8浏览器开发的。我们关注的焦点是一个使用了Java网络启动协议(JNLP)的插件,这个插件存在有溢出漏洞。为了实现利用,我会使用Heaplib来构造ROP链以实现绕过DEP,并获取对目标机器的控制。


Did it Work?

我的exploit目前不能正常工作,我讨厌这个leave指令(如下图)

http://p5.qhimg.com/t0160c7451fa211a31e.png

我进一步确认了一下反汇编。这个错误看起来是我没有访问此DLL的权限,或者是违反了页面的读、写、执行权限。我进一步的研究了一下,并在运行了“!vprot 7c3411a4”命令之后确认了答案,这个命令可以显示我感兴趣的内存权限。

http://p5.qhimg.com/t01b192da5d8aa6e6f1.png


开发exploit

构造恶意输入

Java网络启动协议(JNLP)使应用程序能够在远程服务器上的客户端桌面上启动。事实上JNLP存在一个缓冲区溢出漏洞,发送的请求的结构对于我们来说不是很重要,我们只需关心传递给JNLP导致溢出的docbase参数。

此外,我们将通过Javascript来触发这个利用是基于以下几个原因。首先,我们可以通过Javascript直接控制DOM。DOM是文档对象模型,它可以美化屏幕上显示的页面。它可以呈现HTML、CSS和Javascript,并且还可以访问浏览器的“Syscalls”,比如QuickTime,Flash等。这使得在浏览器中实现这个exploit变得很方便。

这个exploit使用了很多我们已经见到过的利用技术,所以我不会进行详细介绍。但是需要知道,当我们拥有一个溢出漏洞时,就意味着我们可以控制EIP和ESP处的内存。但是,我们还存在着DEP的问题 – 栈不可以执行。由于存在DEP保护,我们被迫使用ROP来解决。我们之前在其他的漏洞利用中使用过ROP技术,即我们去寻找jump 4或其他东西的地址。我们找到有执行权限的地方,然后直接调用这个地址的我们需要的OPCODE。这里的挑战是我们需要的指令可能不止是一、两条。一般来说ROP链会非常大,而且我们还要插入shellcode。令我担心的是,栈可能没有足够的空间来放置我们的全部载荷,我的方法是通过堆,但是这带来了一些额外的挑战。

堆上的第一个挑战是如何定位我们布置的数据。幸运的是,这个问题可以通过Heaplib.js的帮助来解决,它进行堆喷射并且保证地址对齐。堆喷射指的是将我们需要的内存数据放入堆中,以便我们有机会跳入我们需要的数据中。它看起来像这样:

http://p7.qhimg.com/t01586fbdc23666fac9.png

(译注:这张图有误,DEP环境下不再需要NOP sled)

事实证明,Heaplib.js库可以提供非常精确的堆喷射,并且shellcode(译注:作者似乎分不清payload和shellcode的区别,之后也有几处混用。)存在于每间隔2000个字节的地址上,比如0020,2020,4020等(译注:Heaplib是一个用来进行精准堆喷射的库,作者首次提出并使用了Heap Fengshui技术。但因为存在Nozzle,自IE9之后就不能使用了。)

我们面临的第二个问题是怎么把堆转换成栈。这个听起来有点违反常识,但是相信我,马上你就会明白我们为什么要这样做。很多人将这种做法称为“Stack Pivot”(栈翻转),为了说明堆是怎么转化成栈的,我们只需要改变ESP寄存器的值指向我们堆的地址(由堆喷射得来的,存有payload)。这通常通过调用指令

```
XCHNG EAX,ESP;
RET
```

交换EAX的值来实现,这两条指令本身就是一个ROP gadgets。然而,在这里我们不能控制到EAX寄存器,但是我们可以控制EBP寄存器,那么也能够用

```
LEAVE;
RET
```

指令做栈翻转(译注:因为leave等于mov esp,ebp;pop ebp)。我观察了一下有哪些寄存器会被覆盖,发现只有EIP和EBP这两个。我们费这些麻烦的主要原因是因为RET指令会返回到ESP指向的地址,所以我们需要让ESP指向我们在堆上的数据。

下一个要解决的问题是如何执行我们放在堆中的shellcode。请记住,在执行了栈翻转之后,堆现在成了“栈”,至少它看上去是这么一个东西。那我们如何执行shellcode呢?不幸的是现在的Windows使用了DEP保护(译注:准确的说是自IE8开始才启用,是IE版本的问题与Windows版本无关),这就是说某些地方的内存要么是可读、可写的,要么只是可执行的(互斥)。这是为了阻止我们想要做的事——执行存储在栈上的这些指令。事实上有一个方法可以绕过它。在Windows上,Kernel32.dll中的有一个名为VirtualProtect的函数用于更改内存的保护属性。这对我们来说是很有用的,因为如果我们可以对我们shellcode的内存空间调用这个函数,那么就可以将保护从可读写改为可执行,然后执行shellcode。事实上Kernel32.dll是开启了ASLR的,但我们会在“确定使用的参数”部分中处理这个问题。只需要意识到我们有一种改变内存权限的方法 – 当然这需要通过一个ROP链实现。


确定使用的参数

我需要做的第一件事是弄清楚我们将传递给VirtualProtect函数的参数。Alex直接提供给了我们参数,我们可以结合堆喷射的特性直接使用它。

http://p2.qhimg.com/t017c0dd7700f847fbf.png

这些值将是我们放置在栈中以传递给VirtualProtect函数调用的参数。

接下来,我得去寻找VirtualProtect的地址。为此,我使用Windbg来查看不支持ASLR保护的MSVCR71.DLL模块。通过运行命令:`dh msvcr71` 来dump出模块的头,这提供给我们一个VirtualProtect函数的地址表。接下来,我们执行:`dps msvcr71 + 3a000`,它在VirtualProtect之前的表中给出(可能需要多次执行dps)。最后,这给我们提供了一个地址,我们用u poi(7c37a140)确认是我们想要的地址。顺便说一下,这个地址已经在网上列出来了。

接下来我们关心的是确保栈翻转按计划实施。我提到过这取决于你能在这个过程中访问到哪个寄存器的值。只需使用Windbg附加IE进程,然后传递足够多的A来溢出缓冲区(在我们的情况下为200),并观察哪个寄存器会被溢出数据控制。我这样做之后,发现EIP和EBP都被A溢出,EAX保持不变。这意味着我们需要使用`LEAVE;RET`gadgets。

还有很重要的一点就是,所有的ROP gadgets都是在MSVCR71.dll上搜素的,因为它不仅不受ASLR的保护,而且Alex也特意告诉我使用这个。


构造恶意输入

在这一节,我将参考下面的图表来寻找那些特定的ROP gadgets:

http://p4.qhimg.com/t016f858ad679d37221.png

简单来说,ROP gadget只是一些共享内存模块(在我们的例子中是MSVCR71.DLL)中的一个内存地址,我们可以引用它来做一些操作,然后返回。有许多不同的工具可以用于搜索这些地址,Alex推荐了两种不同的选择:第一个叫做Skyrack,第二个叫做msfpescan带有正则表达式。这两种工具对于我想做的事来说都不大好用。于是我用Google找到了一个名为Ropper的开源工具。

所以,我使用了ropper。它很好安装,也很容易搜索复合指令,它会返回所有匹配的实例,而不是只返回一个。要使用的命令是:

```
ropper --file〜/ path / to / msvcr71.dll --search“INSTRUCTION;ret”
```

其中INSTRUCTION是你要搜索的指令。这里是我们需要的指令列表:

```
1.POP EAX,RET;
2.MOV EAX,DWORD PTR [EAX]; RET;
3.CALL EAX; RET;
4.POP EAX; POP EDI POP ESI; POP EBX; POP EBP; RET;
```

前三个可以很容易的通过Ropper找到。最后的指令有点困难。对于这一点,我发现了一个名为Fuzzysecurity的源,它使用了IE8的ROP链执行类似的漏洞利用。我试着跟随他的例子,但是我发现他使用了一个知名的通用ROP链,不过我决定不使用这个。但是,我注意到他找到了POP‘s并且列出了一些gadgets。唯一值得注意的是,他使用的gadgets包含五个POP指令,但我们调用的VirtualProtect只有四个参数。但这没有问题,因为我可以在最后添加一个虚拟的值,之后它会被弹出来。

接下来,将ROP gadgets留给栈翻转。如上所述,我们不能访问EAX – 我们知道在检查WINDBG溢出缓冲区时,发现只有EIP和EBP被覆盖。当我们控制EBP时只需要通过`LEAVE; RET`gadgets来翻转栈。我实际上能够使用Skyrack查找这个,因为如果只是搜索一个单独的LEAVE指令,就有很大的可能性它紧接着一个RET。 我使用WINDBG对地址进行反汇编来验证结果。

我们之所以要有POP(x5); RET,是因为ROP链本身的性质(译注:就是要弹出参数)。考虑以下这张图:

http://p1.qhimg.com/t012debdaaffd864d78.png

当我们从调用VirtualProtect返回时,我们伪造了一个栈帧。这意味着我们需要将VirtualProtect的所有参数POP出来,然后在这些POP之后,我们返回到栈中下一个地址。就是说当我们从VirtualProtect返回时,首先返回到`POP(x5); RET;`它负责删除参数,然后再返回到shellcode。

最初,我打算使用我自己手工生成的ROP链来演示,但是它没有工作。所以我决定采取Chandan的建议,使用Mona.py自动生成的ROP链。

在这里,我还想发出一些推荐给我的“幻影”地址。在课堂上,Alex给我们一些关于我们漏洞利用的结构的“提示”。首先,他说我们的缓冲区溢出应该包含栈翻转。接下来,我们得到一个模糊的提示,在我们的ROP链之前应该有一个地址:1)字节对齐或2)它可能是别的东西 – 我决定只使用Heaplib.js承诺的shellcode地址,因为为什么不呢?我不知道还应该放置什么在那里!最后的提示是,在我们的ROP链和shellcode之后,我们应该包括我们shellcode的“硬编码”地址。这对我没有意义,因为如果它正在执行ROP链,我们应该能够正确地击中shellcode,在我们调用VirtualProtect完成后,我们可以立即开始执行shell。所以基本上,每次我需要的“shellcode地址”使用的是0x0a0a2020。

最后我使用Metasploit来生成我的shellcode。 命令是:generate -t js_be,它提供了可以粘贴到Javscript函数中的shellcode。 一个重要的注意是确保shellcode可以被4整除,以便它可以正确排列。 我的是324字节长,可以被4整除,所以没有什么我需要做的。


未解决的问题

关于这个任务,我还有几个问题没能够弄清楚。下面,我将详细说明我的关注和我的结论,试图说服那些读者,其中至少有一些想法会被纳入这个领域。

1. 我不完全确定在缓冲区溢出时用什么去覆盖EIP的地址。我知道在幻灯片中,讨论了堆上对齐的地址,因为我们要么会命中NOP要么会命中我们的shellcode。但是,我不知道这是如何工作的,因为这个漏洞的整个点是获取和执行我们在堆上的代码。这意味着,如果我们命中的是NOPs或shellcode的开始(基本上除了pivot的一切)我们就会被卡住,因为我们没有移动ESP到我们的位置或改变保护。在与Alex讨论之后,他说用我们的堆栈枢轴覆盖EIP,这就是我所做的。(译注:不知道作者为啥会提出这个问题,估计是没能理解到精准堆喷射的意义所在。)

2. 我认为pivot的地址是我们最初溢出到EIP的地址,但这对我来说没有意义,因为发生在堆栈枢轴(LEAVE; RET)的操作代码只是移动ESP到EBP(译注:move the ESP into EBP???),但是如果我们仍然在栈上,我们就不会将其替换为堆上的地址。(译注:在ret返回之前就已经控制了ebp,可以直接更改成堆地址,为什么非要把EIP覆盖成pivot,实在看不懂作者咋想的。)所以我最终解决了这个问题,用shellcode和pivot的地址溢出初始缓冲区,因为我知道EIP会指向pivot。这以后通过WINDBG确认。

3. Alex基本上告诉我们在我们的ROP链之前插入一个地址,然后立即跟踪它。但是这是不明确的,它的目的是什么 – 我到现在仍然不知道。我获取的信息并没有表明这是必须要去做的。也许只是为了对齐?(译注:插入地址可以对齐???)ROP链后面的第二个地址应该是我们shellcode的“硬编码”地址。再次,我不明白为什么必须要这么做,而不是紧接着放置shellcode(译注:不放地址,你直接把指令ret了能执行吗?),但在这一点上,我盲目地采取了亚历克斯的建议。

4. 为什么我无法执行LEAVE;指令?我的主要想法是EBP有问题或有些东西指向无效的内存。我不是很确定到底是发生了什么,也没有能够解决它。


分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多