-
-
[原创]IE浏览器漏洞综合利用技术:堆喷射技术
-
发表于: 2017-12-4 11:49 10590
-
这是我一年多以前刚刚开始接触浏览器漏洞时写的一篇小总结,之前发到过安全客上。这里转发一遍分享给大家
0.前言
浏览器曾经是漏洞肆虐的重灾区,在IE6时代往往一不留神打开一个页面就会中招。甚至在web渗透圈子中也流传过“拿shell 挂网马”这样一句话。那么这种情况是什么时候得到改观的呢?我个人觉得应该与IE8的出现脱离不开关系,IE8与前几代的最大不同在于它首次支持了DEP和ASLR保护。对漏洞攻防有了解的同学应该知道ASLR早在2007年就已经发布了,而DEP更是可以追溯到xp时代,但是微软出于各种考虑并没有在IE浏览器中启用这些保护,直到IE8的发布。彼时ASLR加上DEP的强强联合曾被认为是牢不可破的马奇诺防线,这种看法在现在看来当然是很可笑的。这让我们认识到漏洞攻防是一种此消彼长的技术、是一种动态平衡,从来不存在绝对的安全。
IE浏览器攻防史上的一个重要事件应该是Pwn2Own 2010的赛场上Peter Vreugdenhil对具有ASLR、DEP保护的IE8浏览器的攻击。在这次利用中,Peter Vreugdenhil使用OOB漏洞结合对象的结构进行信息泄漏,这种手法的思想在现在依然在应用。
从2010年的IE8到2015年的IE11,IE浏览器的攻防技术在短短5年内发展的极为迅速也极为精彩,让我们感受到了攻防对抗的魅力。随着Edge浏览器的发布,IE的攻防对抗可能要落下帷幕了,但是攻防向前发展的脚步却不会停下,Edge其实是延续了这种对抗技术。
在这系列文章中,我们会从最初的IE6“远古时代”开始讲起,会涉及到时间长河中出现的各种利用手段。就像仙果所说,浏览器漏洞利用从最初的“暴力”、“野蛮”逐步发展到了如今的“优雅”和“科幻”。
这系列文章主要目的是做一个技术总结。内容包含比较多,从信息泄露到漏洞利用,从各种缓解措施的绕过到最新保护措施的介绍,在完成这篇文章的过程中我阅读了大量国内外的文档,深感国内的中文资料不是很多,自己也着手翻译了一些。
1.概述
堆喷射是一种极具历史感的技术,第一次出现是在2001年。堆喷射最初的使用目的仅仅是为了给一些漏洞利用提供一个稳定的可以存放shellcode的地址,诸如在栈溢出利用中使用堆喷射放置shellcode然后劫持返回地址进行跳转(无dep情况)。堆喷射第一次在IE浏览器上的应用出现于CVE-2004-1050的exploit中,采用的是极其经典的nops+shellcode的方式。此后结合千疮百孔的ActiveX,诸如栈溢出,漏洞利用的成本着实相当之低。
但是随着2007年ASLR出现之后,这种“老旧”的技术又焕然新生了。尤其是对于IE浏览器、Adobe Reader等软件来说,因为它们支持内嵌执行javascript为攻击者提供了动态分配内存的途径。随着攻防技术的发展,微软以及第三方安全厂商都曾开发过一些堆喷射的缓解措施,所以对于不同的版本来说有不同的堆喷射方法,一旦一种喷射方法被厂商封堵之后,聪明的Hacker们总能想出新的途径进行喷射。
2.基础知识
IE浏览器下的堆喷射一般都是通过js实现的。所谓堆喷射(Heap spray)指的就是通过大量分配内存来填充进程地址空间以便于进一步利用的手段。
在调试堆喷射代码时需要注意的一点是,在调试态下堆内存的分配布局和正常情况下是可能会有差异的,所以不能直接使用调试器附加进程来调试堆喷代码。我们需要等堆喷射完成之后再去附加IE进程,才能得到准确的堆空间布局。同时因为堆的分布不均衡(存在碎片),所以最先分配的一些堆块的地址可能是无规律的,但是如果大量的分配堆块的话,那么就会出现稳定的地址分布。
3.如何调试堆喷射
关于调试器的选择,我个人认为应该使用Windbg,虽然也可以使用Immunity Debugger或是OllyDbg,但是个人觉得调试微软的程序还是Windbg更好用也更稳定一些。
Windbg有一些非常强大的调试命令,这里介绍一些调试漏洞时很有用的命令,这些命令可能暂时用不到但在后面的文章中用到,这里一并给出。
首先是gflags.exe,gflags.exe其实是Windbg自带的一个可执行文件,用于启用一些微软提供的一些调试支持,gflags的原理其实非常简单因为这些调试支持的设置实际上是在注册表中的,我们手动修改注册表的效果其实也是一样的。对于IE漏洞调试来说最有用的调试选项就是UST和HPA。其中,UST是堆分配记录,只要开启了这个选项每一块堆的分配都会记录在进程中,之后如果想要知道一个堆是在哪里分配的只要使用一条命令!heap -p -a即可。HPA是调试堆,只要启用了HPA选项,堆的结构会发生变化,增加额外的检查字段,并且堆的附近内存页变的不再可读可写,这样一旦发生了堆溢出、越界访问或是UAF就可以在第一时间发现并抛出异常。至于更多的选项,我推荐阅读张银奎老师的《软件调试》或者我的博客里简单的记录了一些http://www.cnblogs.com/Ox9A82/p/5603172.html 。
ln 列出附近的符号,这条命令在调试IE漏洞时相当有用,我们后面就可以看到
关于如何调试POC
由于我们是处于浏览器中的缘故,我们并不能够做到直接调试javascript脚本。为此Hacker们想出一些很巧妙的解决方案,其中最常用的就是使用Javascript中的数学函数辅助下断,诸如Math.cos()、Math.sin()、Math.tan()、Math.asin()、Math.acos()、Math.atan()等函数。这些函数的优点是直接对应于jscript!cos、jscript!sin、jscript!tan、jscript!asin、jscript!acos、jscript!atan等调试符号。我们可以在POC中插入这些数学函数,来实现对POC进行调试。
此外如果你对mshtml的一些基本结构诸如CTreeNode、CTreePos有所了解的话,那么调试的效率会更高。
这里我初步的介绍了一些调试浏览器漏洞的小技巧,如果你完全没有接触过漏洞的调试,那么我推荐你看一下泉哥的《漏洞战争:软件漏洞分析精要》。
4.堆喷射需要考虑什么
在介绍实际的喷射手法之前我们先想一想,堆喷射要考虑哪些问题?
最容易想到的就是应该用什么来填充。
其次会想到,多大的填充尺寸可以达到目标地址。
再次是每个基本单位应该要多大,才能够准确又稳定的填充。
接下来我们会看到这些实际的喷射手法就是对这些问题的解决。
5.XP+IE6环境下的堆喷射
IE6浏览器的堆喷射是使用Javascript String对象进行的。
IE6下的堆喷射是最原始的一种,因为IE6那个时期是没有任何漏洞缓解措施的,所以只需要考虑如何分配内存即可。
从代码执行的角度来看,IE6时期我们的利用主要分为两类。第一类是ActiveX类的漏洞,而且以栈溢出为常见。第二类是IE6本身的UAF漏洞。第一类漏洞只需要一个大致的地址+合适的nop跳板就可以实现最终的利用。至于第二类通常会使用一个固定的跳板地址,诸如著名的0x0C0C0C0C,关于它的原理我们之后再讲,这里我们也可以认为它只需要一个大致的地址就可以。
但是由于IE6中javascript的实现,使得字符串赋值给一个变量时并不会开辟新的内存空间(类似于C中的指针取地址),只有当字符串发生连接操作时(substr或是+),才会为字符串开辟新的内存空间。
下面给出了堆喷的测试代码,其中每一个块的大小是0x80000(每个字符两个字节),为什么要取0x80000的大小呢?这是我们在前面提出的问题,其实取别的大小我认为也是可以的,单个块的大小和分配的块数是一种综合的考量,主要是要考虑到内存块分配的速度和内存布局的稳定性。至于为什么这个数是0x40000我也不知道,只能说是前辈们在不断尝试中获得的经验,下面是一个堆喷射的示例代码:
你是否注意到了“0x40000-0x20-sc.length”?
因为js中字符串对象不是简单的Unicode。它是一种复合的字符串结构,称为BSTR,这一数据类型包含有数据长度域、数据域和一个终止符。
4个字节的size域描述字符串的字节数,但是不包含结束符
n个字节的string主体(Unicode格式)
2个字节的x00x00结束符
值得注意的是BSTR并不是javascript定义的,恰恰相反BSTR是微软官方定义的,可以直接在MSDN中查到(https://msdn.microsoft.com/en-us/library/windows/desktop/ms221069(v=vs.85).aspx)
并且可能是通过oleaut32.dll进行分配的
(https://msdn.microsoft.com/en-us/library/ms923851.aspx )
参加MSDN的示例:
我们试图把内存喷射到0x0C0C0C0C,经过简单的计算可知0x0C0C0C0C约为202116108个字节。 500*0x80000约为262144000个字节,262144000大于202116108个字节,因此我们的喷射一定可以到达0x0C0C0C0C。
根据这种算法我们可以得出
任务管理器中内存曲线突出的部分就是堆喷导致的
6.Win7+IE8环境下的堆喷射
IE8相比IE6来说并没有在堆喷射方面做任何的限制,因此我们可以同样通过Javascript String对象进行喷射。
但是在代码执行的角度来看,IE8浏览器支持了ASLR和DEP这两个重要的漏洞环境措施,因此我们堆喷射策略也要进行调整。这一时期的堆喷射关键字是精准,要求能够准确预测到喷射的位置,甚至一个字节都不可以差。至于必须这样的原因,我会在利用部分讲,这里只需要知道此时要求精准喷射。
此外还有一点是IE8下需要把以前的连接语句换成substring()才能实现内存分配,这个没什么好说的。
这一种技术很有可能是Pwn2Own 2010的获胜者Peter Vreugdenhil发明的
(参见http://vreugdenhilresearch.nl/Pwn2Own-2010-Windows7-InternetExplorer8.pdf )
Peter Vreugdenhil发现当堆块进行大量分配的时候,地址分配的熵处于高位并且是对齐的。就是说堆地址的低位不会发生变化,仅有几个高字节是改变的,为此Peter Vreugdenhil给出了一个例子
利用这一点,如果我控制我分配的堆块大小为0x10000。因为低位不变,那么无论如何我们都可以成功的指向我们想要的地址。举个例子,假如我们在第一次运行中,0x0c0c0c0c属于开始地址在0x0c0c0018的堆块、第二次,0x0c0c0c属于开始地址在0x0c080018的块、第三次处于0x0c030018。因为块是对齐的,只要块的大小可以控制为0x10000的基数就可以,比如0×1000、比如0x5000、比如0x10000。这样一来,我们就可以控制0x0c0c0c0c处的内容始终指向rop链第一条语句了。
我在这里使用后面提到的shellcode.substring(0, (0x80000-6)/2);进行分配,分配结果如下图
接下来我们只需要计算目的地址与rop首语句之间的差值即可把eip准确的指向rop链了。
7.IE9环境下的堆喷射
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!