正好在Google搜到了这篇文章,就打算自己翻译一下,也不清楚国内是否有人已经翻译过了。作者是Pwn2Own 2010的获奖者来自荷兰的皮特·维莱格登希尔(Peter Vreugdenhil)。
2010的Pwn2Own是第一次公开亮相的攻破有ASLR+DEP保护的IE浏览器,所以也是我比较关注的一点。当然了就目前(2016)来说,这些都已经是漏洞利用的标配了。所以是一次考古性质的翻译。
ps:我是根据对技术的理解去翻译的,很多地方都有删改,要求精准的可以去看原文。
http://vreugdenhilresearch.nl/Pwn2Own-2010-Windows7-InternetExplorer8.pdf

我决定写一篇关于我在Windows7下攻破有DEP和ASLR保护的IE8浏览器的技术文章。

整个利用过程分为两大步骤

第一步是找出一个确定的dll文件的加载基地址,然后在第二步中会使用到第一步中得到信息来使用一些ret2libc技术去bypass DEP,然后劫持流程去执行shellcode。我暂时还不能公开详细的漏洞细节,但是等微软修补过后我会逐步公开细节的。就像我前面说的那样,我使用了两个exp才实现最后的任意代码执行的结果。

在第一步中,为了获得ie浏览器中一个dll模块加载的基地址,我使用了一个堆溢出覆写了一个UTF8 String(对象?)的尾部结束符(\x00\x00),这样当我读这个字符串的时候就可以读到下一个对象的虚表指针了。当然了,下一个对象也是我精心布置好的。实现这个想法花了我好几天的时间,最终才实现了稳定的利用。

我画一个堆内存的示意图,

黄色的内存是发生溢出的堆块。

绿色的内存是我们分配的Unicode String。其中结尾符\x00\x00由亮绿色表示。

红色的内存是我们精心分配的对象,其中亮红色表示对象的虚表指针。

虽然看着好像很简单,但实际实现却有很多困难。这需要一些技巧才能把内存布局成我们想要的形式。首要的前提是浏览器要存在着一个可以控制的堆溢出,并且我们的这些操作不能让浏览器崩溃掉。然而,经过了一番努力之后,我成功的构造了想要的布局,而且可靠性很高。然后我触发了堆溢出,覆盖了字符串结尾的字符。这样字符串就不再以\x00\x00作为结尾了。如果我们接下来用js函数去读取这个字符串的内容的话,那么就会一直读到下一个\x00\x00为止。这样js函数就读到了下一个对象的虚表地址。虚表地址有什么用呢?虚表在编译时就已经编译到了dll模块中,就是说虚表的地址到模块起始的偏移是确定的。所以根据虚表地址就可以计算出来对象所处的dll模块的基地址。这个信息可以用来编写一个绕过DEP的exp(实质上是bypass ASLR)。如果我对写入溢出的数据拥有完全控制的话,覆写下一对象的虚表就很容易了。但是如果我对溢出数据的控制受到限制的话,我就不能确保可以覆写虚表指针了。

第二步就是想办法绕过DEP的保护了,几个月前,我写了一个绕过IE8下DEP保护的exp,使用的技术是堆喷射+ROP。事实上是混合使用了堆喷射和对象调用伪造,但是我并不确定我是不是第一个使用这种技术的人。在我调试一个IE浏览器的UAF漏洞时,我注意到大堆块的分配是可以预测的。但是不是精准的预测,而是我发现分配地址的最后两个字节总是相同的。在XP系统上大于100字节就会是可预测的。这样一来我们就足够去做堆喷射,并且很有可能猜到喷射到的地址。

举一个例子,下面的代码将不断的分配一些填充有固定内容的堆。这个堆的分配地址应该是开始于0xZZZZZY20的,其中Z的值是随机的,Y则只能为0/4/8/C。因为我分配的单元大小是0x200个字节。

 heap = new heapLib.ie(0x20000);
var heapspray = unescape("%u4141%u4242");
while(heapspray.length < 0x200) heapspray += unescape("%u4444");
var heapblock = heapspray;
while(heapblock.length < 0x40000) heapblock += heapblock;
finalspray = heapblock.substring(2, 0x40000 - 0x21);
for(var i = 0; i < 500; i++)
{
heap.alloc(finalspray);
}

这段代码的执行结果可能是这个样子的

Heap alloc size(0x7ffc0) allocated at 063d0020

Heap alloc size(0x7ffc0) allocated at 06450020

Heap alloc size(0x7ffc0) allocated at 064d0020

Heap alloc size(0x7ffc0) allocated at 06550020

Heap alloc size(0x7ffc0) allocated at 065d0020

Heap alloc size(0x7ffc0) allocated at 06650020

Heap alloc size(0x7ffc0) allocated at 066d0020

如上可见,你得到了堆中彼此相邻的块。

上面例子中的分配大小对于你来说是没有用的,因为这个大小取决于你的具体漏洞情况。

就像代码中写的那样,我使用heaplib来分配字符串,这样更加的方便。

这种技术在Windows7上依然可以使用, 但是分配的次数要比在XP上更多一些。比如在XP上以500为堆喷的次数,0x0a042020作为起始地址。那么到了Windows7上则要进行900次喷射,并且以0x16402020作为起始地址。这样的结果是只要喷射的次数足够多的话,那么我们就能预测出堆分配的地址(而且这个地址可以我们来指定)。

下面来讲解一下简单的UAF漏洞如何进行利用。我发现的UAF漏洞通常都是这样子的:发生UAF的对象在不同行的js中被分配和释放,所以我们有充足的时间去我们指定的数据占位被释放的对象内存空间。

这是我们假设的存在UAF漏洞的代码:

var MyObject = new Object();//分配对象
var MyObjRef = MyObject.SomeProperty; //引用对象
MyObject.CleanUp(); //释放对象
alert(MyObjectRef.parent);//解引用对象

现在我们希望在对象被解引用之前,用我们控制的数据对释放的对象内存地址进行占位。在大多数的UAF漏洞中,发生UAF对象的虚函数都会发生调用。虚表指针就是一个对象的前四个字节的值,所以我们需要进行某种类型的堆喷射可以准确的填充到我们在对象虚表值覆盖的值的位置。

一个好消息是IE对刚刚释放的堆块进行了记录,如果我们申请一个大小大约相同的堆,那么那块内存就会被重新分配(我很怀疑真的是大约相同吗?)。这意味着,如果我们知道释放对象的内存大小,我们就可以用相同大小的我们自定义的数据去分配堆,而且结果会是相同的内存。

想知道对象的大小并不能,只需要对ntdll中的堆分配函数下断即可。我们必须再次分配正确的内存大小,一般我都是通过对div标签数组添加className属性来实现的这一点。这么做的优点是className属性是一个字符串,你可以对这个字符串指定任意大小。这个过程不会导致额外的堆分配(额外的堆分配可能会导致不能正确占位),并且字符串的内容是你自定义的,你可以指定第一个DWORD大小的数据为任意值。唯一的缺点是不能在字符串中使用\x00\x00,但是事实上占位根本用不到\x00\x00。

所以我们需要做一下几件事

  1. 创建数组来存放div元素( var DivArray = new Array(); )
  2. 用50个对象来填充这个数组
  3. 当UAF对象被释放掉后,执行js语句对内存进行占位
  4. 给divs标签添加classname属性( DivArray*i+.className = unescape(“%u4141%u4141......
  5. 解引用UAF对象

这样操作之后的结果会怎么样呢?

最可能的结果是这个样子的:

move eax, [ecx] ecx = our object memory.
call [eax+0x34] eax now holds 0x41414141

到目前为止,我们占位了UAF对象,控制了虚表指针。但是我们该怎么样绕过DEP保护呢?很简单,我们已经控制了eax寄存器。我们也清楚了占位堆的内存布局。我们首先要做的是把eax值指向堆喷的头地址。这样的话就会取堆喷处的0x34偏移的值来call了。但是由于有DEP保护,不能直接的在堆上执行代码。我们需要做的就是想办法调用VirtualProtect函数,把shellcode所处的内存的属性由可读可写设置为可读可写可执行。
举个例子,假设我们已经发现一些如下的指令序列(作为gadgets存在)

0x6ff02348 :

mov ecx,eax

call [eax+10]

and another that goes like:

0x6FF01234 :

push [eax+70]

push [eax+60]

push [eax+50]

push [eax+40]

push [eax+34]

push [eax+20]

call [ecx+14]

.....

.....

retn

那么,如果我们这么去设置堆喷的堆内容的话:(根据引用顺序排序)

  1. +0x34:0x6FF02348
  2. +0x10:0x6FF01234
  3. +0x14:0x7c801ad4 (VirtualProtect)
  4. +0x20:堆喷的首地址
  5. +0x30:0x200(单个堆喷块的大小)
  6. +0x40:0x40(READWRITEEXCUTE)
  7. +0x50:无意义的值,占位用

这样的话我们就成功的调用了VirtualProtect函数把内存的属性改成了可执行的。这种方法在XP上能够得到很好的执行,因为XP系统上我们可以准确的知道VirtualProtect的地址。在Windows7上利用我们需要更多的创造性。

我们在不知道kernel32.dll模块的准确基地址的情况下该如何调用VirtualProtect呢?IE的许多dll模块包含有ATL库,这些ATL库中会调用VirtualProtect函数。这意味着VirtualProtect函数处于我们已知的一个固定的偏移(相对dll基址)。假如我们知道dll加载的基地址是0x6fff0000,0x6fff1288是VirtualProtect函数的地址。我们需要做的就是在诸如call [eax+8]这样语句之前把eax设置为0x6fff1280。这可以通过想上面那样利用代码并设置堆喷的内容来实现。当我使用这种方法时,我经常把我的堆喷内容设置为连续增长的值,比如:

var pattern = unescape(“%u0000%u0001%u0002%u0003%u0004%u0005%u0006........”)

通过这种方法,就可以更容易的找出你的字符串中需要编辑的值。

本质上,我们要做的全部事情就是连接起已经准备好的这些代码块(gadgets)。但是只能使用call或是jmp去连接,直到成功的执行了VirtualProtect。然后我们的堆喷就具有了可执行权限。如果我们的执行流中向栈中压入的参数多于VirtualProtect函数需要的,那么返回堆栈将会被破坏,并且会结束掉我们的流程。

Peter Vreugdenhil

翻译 by:Ox9A82

【考古向翻译】Pwn2Own 2010 Windows 7 Internet Explorer 8 exploit的更多相关文章

  1. 关于windows 下每次打开IE 8都弹出欢迎使用Internet Explorer 8 弹窗的关闭方法

    今天笔者在安装完windows 操作系统后,发现了一个问题,即每次打开IE 8浏览器,都会弹出一个欢迎界面: 弹窗标题为:设置windows Internet Explorer,具体内容如下图所示: ...

  2. How to Uninstall Internet Explorer 11 for Windows 7

    Internet Explorer 11 is the newest version of Microsoft's web browser, but not everyone is a fan. If ...

  3. 企业IT管理员IE11升级指南【2】—— Internet Explorer 11 对Adobe Flash的支持

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  4. 如何使用BHO定制你的Internet Explorer浏览器

    原文出处:Browser Helper Objects: The Browser the Way You Want It一.简介 有时,你可能需要一个定制版本的浏览器.在这种情况下,你可以自由地把一些 ...

  5. 必应翻译:让Windows上的翻译不再是难事

    文章译自:Translations Made Easy on Windows 比方说今年夏天你想出国旅行,计划去一趟德国.你的行程很丰富:参观慕尼黑和柏林,乘坐游船沿莱茵河而下,再品尝几串摩泽尔的葡萄 ...

  6. IE中无法执行JS脚本 解决WINDOWS SERVER 2008弹出INTERNET EXPLORER增强安全配置正在阻止来自下列网站的内容

    在默认状态下,使用Windows Server 2008系统自带的IE浏览器访问网页内容时,我们时常发现“Internet Explorer增强安全配置正在阻止来自下列网站的内容”的提示导致不能打开网 ...

  7. 【翻译习作】 Windows Workflow Foundation程序开发-第一章05

    1.3      开发我们的第一个工作流 也许你曾经在这样的产品经理手下搞过开发:他总是在你身边转悠,并不时的问一句“你还没做完吗?”.在这一部分,我们将用一个简单的Windows Workflow程 ...

  8. 【翻译习作】 Windows Workflow Foundation程序开发-第一章04

    1.2.3  Windows Workflow运行时 从Windows Workflow的角度看,可以将工作流活动当成是交给一个工作流处理器去执行的一系列指令或操作码.在Windows Workflo ...

  9. 【翻译习作】 Windows Workflow Foundation程序开发-第一章03

    1.2.2.Visual Studio 2005扩展包 微软也为Windows Workflow开发者提供了Visual Studio 2005扩展包.扩展包将许多功能集成到Visual Studio ...

随机推荐

  1. Windows下CVSNT安装配置

    首先要说明:X64下安装此软件会报 “cvs [login aborted]: WIN-4H9CRJO1TRA\Administrator: Switch to user failed due to  ...

  2. docker:轻量级图形页面管理之Portainer

    docker:轻量级图形页面管理之Portainer 原创甘兵2018-03-05 14:26:56评论(8)2586人阅读   1.介绍 docker 图形化管理提供了很多工具,有Portainer ...

  3. array_merge 优化调整

    function dealed_array_merge($a,$b){ if ($a && !$b){ return $a; } if (!$a && $b){ ret ...

  4. JS--数组和字典

    一.JS数组 JavaScript中的数组类似于Python的列表 a = [11,22,33,44]  常见功能: obj.length 数组的大小 obj.push(ele) 尾部追加元素 obj ...

  5. unity解析json的两种方式

    一直比较钟情于json,用来做数据交互,堪称完美!下面简单说一下unity使用C#脚本如何解析json数据吧. 一.写解析类,借助于JsonUtility.FromJson 直接给个例子吧 1.jso ...

  6. P1783 二分并查集写法

    并查集 + 二分 我是 并查集 + 二分 做的QVQ 思路:两两枚举点之间的距离,sort排序,使距离有序.二分答案,每次判断是否符合条件,然后缩小查询范围,直到满足题目要求(保留2位小数精度就为 0 ...

  7. [CQOI2009]DANCE跳舞(ISAP写法)

    https://daniu.luogu.org/problemnew/show/3153 #include<queue> #include<cstdio> #include&l ...

  8. Swiper点击后自动轮播停止情况

    用户操作swiper之后,是否禁止autoplay.默认为true:停止. 如果设置为false,用户操作swiper之后自动切换不会停止,每次都会重新启动autoplay. 操作包括触碰,拖动,点击 ...

  9. Hbase建模选择

    日期 2017年3月17日 HBase建模记录 OLTP 应用场景: OLAP 应用场景: 语音分析系统的应用场景 基于HBase的建模考虑 1.话单为主来考虑hbase的rowkey的生成规则: 1 ...

  10. [转载]浅析为什么char类型的范围是 —128~+127

    http://blog.csdn.net/daiyutage/article/details/8575248 在C语言中, signed char 类型的范围为-128~127,每本教科书上也这么写, ...