JITBHDCPaperchs.docx

上传人:b****6 文档编号:3310447 上传时间:2022-11-21 格式:DOCX 页数:12 大小:190.60KB
下载 相关 举报
JITBHDCPaperchs.docx_第1页
第1页 / 共12页
JITBHDCPaperchs.docx_第2页
第2页 / 共12页
JITBHDCPaperchs.docx_第3页
第3页 / 共12页
JITBHDCPaperchs.docx_第4页
第4页 / 共12页
JITBHDCPaperchs.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

JITBHDCPaperchs.docx

《JITBHDCPaperchs.docx》由会员分享,可在线阅读,更多相关《JITBHDCPaperchs.docx(12页珍藏版)》请在冰豆网上搜索。

JITBHDCPaperchs.docx

JITBHDCPaperchs

INTERPRETEREXPLOITATION:

POINTERINFERENCEANDJIT

SPRAYING

DionBlazakisdion@

翻译:

espzj

摘要

随着远程漏洞的逐渐减少以及边界防护的标准化,针对远程客户端的攻击是攻击者下一个最好的选择。

如今的Windows操作系统通过使用DEP和ASLR等技术已经逐渐消除了针对客户端漏洞的利用。

本文将介绍两种新颖的技巧来绕过DEP和ASLR。

这些技巧利用了在浏览器内通常可访问的高级脚本解释器和虚拟机所暴露出来的攻击面。

第一个技巧,pointerinference,用于在使用了ASLR的情况下找出ActionScriptinterpreter内shellcode串的内存地址。

第二个技巧,JITspraying,通过利用ActionScriptJIT编译器可以预测的行为把shellcode写到可以执行的内存地址来绕过DEP。

最后讨论了未来的研究方向和翻译器(用于针对这些技巧)的对抗措施实现。

介绍

发现和利用远程漏洞的困难性激发攻击者把他们的资源用于发现和利用客户端的漏洞。

客户端攻击者的汇聚促使微软实施健壮的对抗技术,使得针对这些漏洞的利用越来越困难。

SotirovandDowd【1】详细介绍了每个对抗技术以及它们在直到Windows7RC的各个Windows系统下的默认配置。

他们介绍了一些可以用来绕过这些保护的技巧以及微软公司的设计选择是怎样影响绕过这些保护措施的具体细节的。

贯穿文章的一件事情就是针对浏览器的攻击利用是多么的成熟—攻击者可以使用多个插件(plugins),选择特定的可利用插件,来创建一个可靠的利用情景。

经典的web浏览器,因为插件而开了一道缝隙,被设计成不可能有更多可能被利用的地方。

它需要一个健壮的解析器来解析和尝试对版本6的提升。

随着Web2.0的到来,浏览器必需包含一个高性能的脚本环境来重写这些动态解析的页面。

暴露给这些运行时脚本库在继续增加。

另外,大多数浏览器都利用了最近的JIT和垃圾搜集技术来加速脚本的执行。

所有这些攻击面以及我们还没讨论的插件都是普遍安装了的。

RIA(RichInternetApplication,富互联网应用系统)越来越多,Adobe目前牢牢地掌握了Flash市场。

安装了web浏览器的主机有99%都安装了FlashPlayer。

Sun公司的JRE(JavaRuntimeEnvironment)是另一个普遍安装了的解释器。

微软公司的Sliverlight是一个基于运行时.NET和工具的RIA框架。

Silverlight仍在试图获得市场份额,在将来可能会是一个竞争者(例如,NetflixOn-Demand已经开始使用这个技术)。

这些插件都需要一个复杂的解析器并且暴露了更多的可被攻击者利用的攻击面。

例如,AdobFlashPlayer实现的特色包括一个非常大的GUI库,一个JIT3Dshadowlanguage,一个RMI系统,一个基于ECMAScript的JIT虚拟机,对嵌入式pdf的支持以及多个声音和视屏嵌入和流的选项。

所有这些特色默认都可以被那些幸运的攻击者所利用。

考虑到这一点,投入时间和精力发展特定应用程序的技巧对于利用浏览器生态系统内的漏洞是有帮助的。

DEP和ASLR对于漏洞利用人员来说是一个真正的挑战。

DEP使得定位shellcode变得困难。

(原文:

DEPmakeslocatingshellcodedifficult。

我认为应该改为:

ASLRmakeslocatingshellcodedifficult,ASLR使得定位shellcode变得困难);攻击者必需首先找到具有可执行权限的页面并且要能够在具有写权限时对其进行写操作,同时要能够定位它的地址。

有一种选择就是,攻击者可以找到一些代码重新使用—正如基于return-orientedprogramming【3,4】的攻击那样。

ASLR通过混淆加载映像的基地址来对抗这个选择。

请参看Sotirov和Dowd【1】的文章来详细了解在不同Windows操作系统上ASLR和DEP的具体实现。

现在,我们假定ASLR接近完美---也就是攻击者不能猜出加载映像,堆,堆栈的地址。

本文把焦点集中于两种绕过这些安全措施的技巧上:

pointererinference和JITspraying。

Pointerinference是通过和目标软件的”正常”(例如,不是通过利用漏洞)交互来恢复内部对象的内存地址的行为。

以AdobeFlashPlayer作为例子。

我将展示如何通过一个脚本来判定一个位于FlashPlayer的ActionScript解释器内部的字符串对象的地址。

JITspraying类似于heapspraying;攻击者将分配许多可执行的页面,这些页面包含了攻击者能够影响的数据。

我将展示如何构造一个ActionScript脚本,当被Actionscript引擎即时编译时,构造出一个stage-0的shellcode来加载和执行nextstage的shellcode(定位于一个ActionScript字符串)。

然后,我将讨论结合使用这两项技术来攻击WindowsVista的可能性。

最后,讨论了一些对抗措施和未来的研究方向。

PointerInference

把控制权从EIP转到exploit在地址空间随机化的情况下变得越来越困难。

即使从一个崩溃的POC得到EIP的控制权也很困难。

证实一个漏洞存在有时是相当容易的,使用heapspray在一个已知的地址放置攻击者构造的结构就可以轻易实现。

但是对漏洞利用者不幸的是,heapspray并不总是非常可靠(而且有时并不总是可行—如果攻击者不能分配大的堆对象)。

另外,针对heapspray的对抗措施(mitigation)已经出现在学术界【10】而且微软公司在它们的EMET工具【9】中有一个相当简单的消除措施(把heapspray常用的页面映射到进程开始处)。

可靠的利用要有在一定的范围内动态地得到运行时对象的堆地址的能力。

脚本执行环境是pointerinference的完美目标—对象通常存在于堆,并且运行时语言和库提供了多种方法来操作和检查对象。

另外,脚本语言是动态类型语言,因此内置的容器对象通常是不同类的—-存引用对象和存值对象的结构通常是一样的。

我们的目标是找到一种方法,以在解释器或虚拟机内判定脚本对象的内存地址。

提取包含有shellcod的JavaScript字符串的地址,并不是这个技巧的唯一用处,但却是一个好的利用的基本模块。

目标VM包括(ECMA|Java|Action)Script,Java,Python,Ruby,PHP,以及.NETCLR。

浏览器内的Javascript引擎,AdobeReader内的JavaScript引擎,以及AdobeFlashPlayer内的ActionScript引擎等在大多数浏览器安装后都可以使用(如果他们按装了Adobe插件并且没有禁用JavaScript)。

本文我将显示如何在遍历ActionScrip字典对象时,利用对象的顺序得到在ActionScript虚拟机内某个对象的内存地址。

我选择在这个POC中使用FlashPlayer有多个原因:

相对于其他的,我逆向它的时间最多,它是跨平台的—不管浏览器,对插件来说都是同样的代码基的,最后,但并不是最不重要的是,Adobe已经开放了它们的ActionScriptcompiler的源代码。

为了了解这个技巧的细节,首先我将描述对象在内部是如何被解释器所存储的,然后介绍了解释器是如何实现内置的字典容器的。

ActionScript对象的内部表示

在讨论揭露ActionScript对象地址的方法之前,先介绍下解释器内部是怎样存储对象的。

当执行的脚本创建一个ActionScript对象时,解释器基于对象的类型来进行相应的操作。

如果对象是比较小的基本对象,比如整数(integer)或者布尔(boolean)类型,就存储它的值。

对于doubles,strings,和class则存它们的引用—解释器会分配一个足够大的缓冲区来存储对象并且保存一个指向这个缓冲区的指针。

ActionScript是一种动态类型语言。

动态类型语言在编译时并不给值赋予类型。

动态类型语言并不把类型表示加入语言并在编译时加入类型限制,而是提供运行时函数来检查和比较对象类型。

解释器确保所有在操作时的操作数都是合法的。

当解释器检测到类型不匹配时(例如,查询一个实例并没有实现的方法时),ActionScript要么会抛出一个异常或者隐式强制转换。

这种运行时类型语言要求对象堆不光要存储对象的类型,还要保存对象的值。

为了处理这种运行时类型要求,ActionScript解释器使用带标签的指针(taggedpointers)--在内部,这些对象被称作”atom”,来表示内部对象。

在使用同样大小的内存单元的情况下,Taggedpointers是用来区分存值对像和存引用对象的常用实现方法。

TaggedPointer用最低的几个比特位存储类型信息,用高位来存储特定类型的值。

如图1所示,ActionScript的atom是32bit宽的;它分配3bit来存类型信息,用29bit来存值。

图1

例子可能会有助于解释是如何工作的。

拿下面的ActionScript声明来说:

varx=42;//Integeratom

vary=“blackhat”;//Stringatom

varz=newDictionary();//Objectatom

varb=true;//Booleanatom

变量x是一个值为42的局部变量。

解释器会在当前域内创建一个局部变量和值42的映射。

正如上面所描述的,这个值在内部将会被表示为一个atom。

整数atom能表示-228到228-1的值,是通过创建时把值左移3bit来给类型标签留存储空间的。

整数atom的标签是一个6,如果图1所示。

用Python表示这个过程如下:

>>>defatomFromInteger(n):

return(n<<3)|6

>>>'0x%08x'%(atomFromInteger(42),)

'0x00000156'

String和Objectatoms是“引用”atom-它们存储指针,指向解释器堆上面的垃圾回收内存。

把y和z转换为atom需要先为它们分配一块内存来保存值,然后用这个内存地址的值来创建atom。

下面是一个用Python操作z的例子—额外的call是模拟的,不要让它们分散你的注意力。

>>>defatomFromObject(obj):

return(obj&~7)|1

>>>a=actionScriptHeapAlloc(size_of_Dictionary)

>>>Dictionary.initialize(a)

>>>'0x%08x'%(atomFromObject(a),)

'0x00c8b301'

介绍内部如何表示的目的是用于解释值和引用(内存地址,指针)都被解释器用作atom。

接下来,我将解释ActionScript字典类是如何实现和使用的。

ActionScript字典的实现

内置的ActionScript字典类揭示了一个关联的图数据结构。

当在ActionScript脚本内部使用时,它提供一个接口,把任何ActionScript对象同任何其他的ActionScript对象关联为key/value关系。

类似于Python字典,字典对象可以通过方括号来查询。

另外,用户可以通过遍历字典来操作每个key/value对。

遍历的顺序并没有通过API的定义来指定,同时是不依赖于实现的细节的。

使用例子:

vardict=newDictionary();

dict[“Alpha”]=0x41414141;

dict[true]=1.5;

vark;

for(kindict)

{

f(dict[k]);

}

FlashPlayer的字典类在内部通过hashtable来实现。

Hashtable从keyatom而来,并且把key和valueatom一起存到表中。

当遍历字典时,hashtable从最低的hashvalue遍历到最高的hashvalue。

最后的细节就是hash函数是如何工作的,它是如何处理碰撞的,以及hashtable是如何增长的。

Hashtable的大小总是2的幂次。

这样做有两个原因:

hash函数是一个快速的掩码操作函数,平方探测依赖于2的幂次的表。

当一个表中的空单元低于总大小的20%时,hashtable就要增长到2的下一个幂次(也就是表的大小要乘以2)。

为了把表变大,先要分配一个新表,所有的条目都将要被重新hash并插入到新表中。

Hashtable的实现泄露了integer(值)atom和object(引用)atom的顺序---objectatom被用于直接同intergeratom比较。

Hash函数将会把最高的某些位给删除掉,但是一个大的hashtable将会使用剩下的大多bit位。

表中的顺序就泄露了引用atom的内存地址。

整数筛选

因为整数使用它们的值作为在hashtable中的键值(key)(当然,最高的某些bit位将会被屏蔽掉),我们可以通过在遍历hashtable时测量新的对象被发现的位置,来判定某些ActionScript对象的atom值。

通过记录新插入的对象之前和之后的整数,我们可以得到新对象的atom的一个范围。

因为Objectatom只是指针(有3bit的修改),我们可以在hashtable增大时得到指针的bit位。

为了避免hash碰撞的问题,我们执行两次测试:

一次是所有的偶数,一次是所有的奇数(达到2的某次幂-随着我们发现的bit位数而增大)。

在创建完字典后,把victimobject插入两个字典。

字典中和关键字关联的值并不对此造成任何影响---只与关键字和它们的顺序有关。

接下来,通过使用for-in结构查询字典,记录下最后访问的关键字并且在当前关键字是victimobject时退出。

现在我们有两个整数值(对偶数和奇数字典分别有一个在victimobject之前的最后一个整数)。

这两个整数应该相差17。

原因在于这是线性探查;当hashtable插入时遇到碰撞,它使用二次探查来找出一个空的单元。

第一个尝试位于(hash(victim_atom)+8),但是总是和-2n+8=2(n+4)或(2n+1)+8=2(n+4)+1碰撞。

第二个尝试是(hash(victim_atom)+17),这次尝试总是会成功,-2n+17=2(n+8)+1或者(2n+1)+17=2(n+9)。

唯一使得这两个整数相差不是17的情况就是这个探查被包装了。

否则,小的整数来自于并不存在碰撞的字典。

当两者相差不是17的时候,较大的值来自于不存在碰撞的字典。

现在我们有一个整数,当转换为atom时比victimatom小8。

最后,可以从整数得到victimatom,x:

(x<<3+8)或者((x+1)<<3);

那只是一小部分;来查看执行过程:

//First,createtheDictionariesvareven=newDictionary();

varodd=newDictionary();

//Now,filltheDictionaryobjectswiththeintegeratomsvarindex;

for(index=0;index<8;index+=1){

even[index*2]=true;

odd[index*2+1]=true;

}

在插入intergeratom后,堆看起来将会如此:

图2

接下来,插入引用对象来泄露地址:

varvictim=“AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”;

even[victim]=true;

odd[victim]=true;

图3:

插入victim对象之后的堆

在图3中,可以看到victimobject是怎样被包围的(表有16个单元,因此17在模16后和1有碰撞)。

最后一步就是遍历两个字典来找到最后一个出现在victimobject对象之前的整数。

varcurr,evenPrev,oddPrev;

for(currineven)

{

if(curr==victim){break;}

evenPrev=curr;

}

for(currinodd)

{

if(curr==obj){break;}

oddPrev=curr;

}

在执行完上面一段代码之后,evenPrev将包含值0(atom:

0x00000006),oddPrev将包含值1(atom:

0x0000000E),假定evenPrev和oddPrev相差17(hash表为一个更合理的大小,比如,4百万)。

evenPrev小一些,这就意味着我们可用通过把evenPrev(值为0,正如在ActionScript中大家所看到的一样)加1然后左移3位来得到vimtimobject的最低几位地址。

结果就是0x00000008。

和victimobject的地址的最低几位匹配。

提取出内存地址泄流并不唯一,并且其他用于推测地址信息的变量也不难想象。

如果我们限制用数据结构泄露,内部id和hashfunction(和ActionScript技术相反,.NET和Java并不能通过脚本直接获取hash,参见.NET和Java)。

这些函数需要一个独一无二的堆对象并且这些地址也能够被使用。

我为这个过程提供了ActionScript样本例子,并且在AdobeFlashPlayerplug-in(NPSWF32.DLLandFlash10c.ocx)版本10.0.32.18and10.0.42.34上验证过。

JITSPRAY

数据执行保护(DEP)使得执行构造的shellcode相当困难—堆栈和默认堆都被标记为不可执行。

据我所知,已公开的绕过DEP的最好的方法就是找到一个已加载了的,没有使用ASLR的DLL,然后使用DLL中的代码来构造return-orientedshellcode【3,4】来关闭进程的DEP,或者分配可执行的内存并把nextstageshellcode拷贝到已分配的地方。

现代的解释器实现了一个即时(Just-In-Time,JIT)编译器来把解析的输入或者字节码转化为机器码以加快执行速度。

JITspray就是迫使JIT引擎把嵌入的shellcode写到可执行页面的过程。

Shellcode通过正常的JIT指令输入。

例如,一个JavaScript语句如“varx=0x41414141+0x42424242;”可能被编译为在可执行映像中包含2个4字节的常量(例如,“moveax,0x41414141;movecx,0x42424242;addeax,ecx”)。

从这写常量中间的某个位置开始将会执行一个完全不同的指令序列。

本节的剩余部分将介绍一个针对AdobeFlashPlayerActionScriptbytecodeJIT的例子。

结尾的结果是一个Python脚本和ActionScript脚本。

Python脚本生成ActionScript。

ActionScript在加载和字节码引擎开始JIT编译后,将在堆上展示一个可执行的页面。

当这个可执行页面被输入一个已知的偏移量(0x6A,对于下面的例子代码来说)后,将执行一个stage-0的shellcode使得剩下的页面可以读写并执行,并且把nextstage的shellcode从一个可以在编译前修改的ActionScriptString中拷贝过来。

关键点就是JIT是可以预测的并且必须拷贝一些常量到可执行的页面。

给一串一致的语句,这些常量可以编码小的指令并且能够把控制流转移到下一个常量的位置。

Development

在FlashPlayer中调用VirtualProtect的位置下一个断点,我们可以观察从ABC(ActionScriptByteCode)字节码到JIT代码的生成过程。

通过实验,我发现一个长XOR表达式(a^b^c^d^…)将会被编译为一个非常精简的XOR指令集。

例如,在JIT编译后

vary=(

0x3c54d0d9^

0x3c909058^

0x3c59f46a^

0x3c90c801^

0x3c9030d9^

0x3c53535b^

将转变为:

03470069B8D9D0543CMOVEAX,3C54D0D9

0347006E355890903CXOREAX,3C909058

03470073356AF4593CXOREAX,3C59F46A

034700783501C8903CXOREAX,3C90C801

0347007D35D930903CXOREAX,3C9030D9

03470082355B53533CXOREAX,3C53535B

现在,如果从0x0347006A开始执行:

0347006AD9D0FNOP

0347006C54PUSHESP

0347006D3C35CMPAL,35

0347006F58POPEAX

0347007090NOP

0347007190NOP

034700723C35CMPAL,35

034700746AF4PUSH-0C

0347007659POPECX

034700773C35CMPAL,35

0347007901C8ADDEAX,ECX

0347007B90NOP

0347007ED930FSTENVDS:

[EAX]

0347007C3C35CMPAL,35

这是一个很流行的GetPC方法-使用浮点状态保存【8】

ActionScript允许动态加载(和运行时生成)字节码,使用这个技巧,我们就可以通过重复加载一个给定的字节码文件来在堆上生成构造的JIT代码。

这个就是JITspray;在合理的猜测并且幸运的情况下,攻击者在ASLR和DEP存在的情况下可以执行shellcode。

最后一节,我们将看看如何使用。

上面给的例子中,注意到控制从一个DWORD转移到下一个只用了一个字节。

利用单字节XOREAX机器码,cmpal在语义上也是一个NOP,并且不需要JMP指令所需要的两个字节。

二者结合

在讨论Pointerinference中没有说明的一点就是通过泄露的指针所指向的值。

攻击者通过地址泄露能够得到什么呢?

攻击者通过泄露的指针能够控制的值是什么呢?

对于Objectatom来说,指针所指向的C++实例包含了很难被控制的域。

对于Stringatom来说,指针所指向的C++对象包含一个长度域和一个指向字符串缓冲区的指针。

这在某些例子下肯定是有用的,但它并不是直接指向string缓冲区的指针。

最后,它只是一个指向堆的指针。

为了找到和使用这个指针,我们必须理解F

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 电脑基础知识

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1