漏洞测试

 

1. 漏洞描述 IE浏览器是windows自带的浏览器,因用户量巨大所以其安全性一直被大家广泛关注。这次主要对漏洞CVE-2013-0025进行分析。这个漏洞影响微软的IE 8浏览器。漏洞的形成原因是因为当将指定元素的white-space属性设置为pre-line时(即为:合并空白字符序列,但保留换行符,此值为CSS2.1版本中增加的属性),会导致指定的元素对应的CTreeNode节点连入CTreePos结构中。并且通过这个结构形成一个ChtmRootParseCtx对象指向的双向链表。此时当这个元素被释放的时候,这个链表中对应的CTreePos不会被释放,最终导致了uaf。成功利用后可以达到任意代码执行的效果。 2. 测试环境 windows xp sp3 microsoft internet explorer 8 3. 漏洞分析 首先我们看一下这个漏洞最精简的poc。只需要两行javascript代码就可以触发,当然你要是想利用还是需要做很多工作的。在此就不贴出具有攻击性的代码了,但是网上已经存在公开的利用模块了。 代码: <!doctype html>

 

 

需要注意的一点是

标签之间有一个空格,这个是关键。只有存在空格后面的CSS属性才会起作用。poc运行效果如下。 代码: (960.bfc): Access violation – code c0000005 (!!! second chance !!!) eax=00000000 ebx=0020ca28 ecx=02d700b2 edx=00000000 esi=0208dd68 edi=00000000 eip=6363fcc6 esp=0208dd3c ebp=0208dd54 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mshtml!CElement::Doc+0x2: 6363fcc6 8b5070 mov edx,dword ptr [eax+70h] ds:0023:00000070=???????? 可以看到此处是一个内存读取了无效地址的异常,先简单看一下上下文环境。 代码: 0:008> uf mshtml!CElement::Doc mshtml!CElement::Doc: 6363fcc4 8b01 mov eax,dword ptr [ecx] 6363fcc6 8b5070 mov edx,dword ptr [eax+70h] 6363fcc9 ffd2 call edx 6363fccb 8b400c mov eax,dword ptr [eax+0Ch] 6363fcce c3 ret 可以看到在我们读取指定内存之后紧接着就会调用6363fcc9地址出的指令call edx,可以明显看到eax是当前的CElement对象的虚函数表,此处是想要调用其虚函数表中偏移0x70处的函数。我们看一下堆栈。 如图所示可以看到问题出现在SlayouRun对象调用GetInnerNodeCrossingBlockBoundary函数时发生的。我们来看一下这个函数。其出现问题的代码如下。 经分析发现esi所指向的就是CparamElement对象所对应的CtreeNode指针。为了分析这个元素到底是什么我们下如下断点。因为漏洞主要相关的对象在这里为

所以对其的创建和释放函数下断点。 代码: 0:008> bl 0 e 635d478c 0001 (0001) 0:**** mshtml!CParaElement::CreateElement+0x16 “.printf \” = create P Element at 0x%08x\”,eax;.echo;” 1 e 635a31e6 0001 (0001) 0:**** mshtml!CTreeNode::CTreeNode “.printf \” = create CTreeNode Element at 0x%08x\”,ecx;.echo;g;” 2 e 635cdd41 0001 (0001) 0:**** mshtml!CDivElement::`scalar deleting destructor\’ “.printf \”release P Element at 0x%08x\”,ecx;.echo;g;” 3 e 63662f97 0001 (0001) 0:**** mshtml!CTreeNode::Release “.printf\” = release CTreeNode Element at 0x%08x\”,edx;.echo;g;” 4 e 63718f8a 0001 (0001) 0:**** mshtml!SLayoutRun::GetInnerNodeCrossingBlockBoundary+0x3e 重新运行调试器。可以看到如下输出。 代码: 0:016> g … … = create P Element at 0x030445a0 eax=030445a0 ebx=00202850 ecx=7c9301bb edx=00000028 esi=00202788 edi=02d0db70 eip=635d478c esp=0208fac8 ebp=0208facc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mshtml!CParaElement::CreateElement+0x16: 635d478c 8bf0 mov esi,eax 此时创建P对象。其地址为0x030445a0。然后继续mshtml!SLayoutRun::GetInnerNodeCrossingBlockBoundary会运行三次之后断在如下位置。 代码: Breakpoint 4 hit eax=00000000 ebx=00000001 ecx=00237148 edx=00000040 esi=0020cdc0 edi=0208dff8 eip=63718f8a esp=0208dfd4 ebp=0208dfe4 iopl=0 nv up ei pl nz ac po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212 mshtml!SLayoutRun::GetInnerNodeCrossingBlockBoundary+0x3e: 63718f8a e8c1eeffff call mshtml!CLayoutBlock::IsBlockNode (63717e50) 此处就是上面我们分析的代码,esi中的值是CtreeNode的指针。而其正好是我们刚才运行输出的创建P对象后创建的第一个CtreeNode节点。打印日志如下。 代码: … … = create CTreeNode Element at 0x0020cd10 = release CTreeNode Element at 0x0020cdc0 … … 现在可以确定一下这个CtreeNode中的对象是不是P。修改断点并重新运行。 代码: 0:008> bu mshtml!CParaElement::CreateElement+0x16 “.printf \” = create P Element at 0x%08x\”,eax;.echo;” = create P Element at 0x02d1b1d0 eax=02d1b1d0 ebx=002028f0 ecx=7c9301bb edx=00000028 esi=00202828 edi=02d0dda0 eip=635d478c esp=0208fac8 ebp=0208facc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mshtml!CParaElement::CreateElement+0x16: 635d478c 8bf0 mov esi,eax 此时停在了创建P对象的函数中,然后修改断点。 代码: 0:008> bu mshtml!CTreeNode::CTreeNode “.printf \” = create CTreeNode Element at 0x%08x\”,ecx;.echo;” breakpoint 1 redefined 主要目的是使其可以停在后面的创建CtreeNode的函数CTreeNode::CTreeNode中。 代码: breakpoint 1 redefined 0:008> g = create CTreeNode Element at 0x0020cd58 eax=0020cd58 ebx=00000000 ecx=0020cd58 edx=0000004c esi=001fdca0 edi=02d1b1d0 eip=635a31e6 esp=0208fa8c ebp=0208faa4 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 mshtml!CTreeNode::CTreeNode: 635a31e6 8bff mov edi,edi 这时我们确定了创建的p的地址为0x02d1b1d0、CtreeNode对象的地址为0x0020cd58。 代码: 0:008> dd 0x0020cd58 0020cd58 00000000 00000000 00000000 00000000 0020cd68 00000000 00000000 00000000 00000000 0020cd78 00000000 00000000 00000000 00000000 0020cd88 00000000 00000000 00000000 00000000 0020cd98 00000000 00000000 00000000 00000000 0020cda8 eaa8c615 ff0c0000 002300a7 00000000 0020cdb8 ffff042f ffffffff 00000061 00000000 0020cdc8 00000000 00000000 0020cd10 0020cd68 对CtreeNode下内存访问断点。 代码: 0:008> ba r4 0x0020cd58 0:008> g Breakpoint 5 hit eax=0020cd00 ebx=00000000 ecx=0020cd58 edx=00000000 esi=00000008 edi=02d1b1d0 eip=635a321c esp=0208fa84 ebp=0208fa88 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 mshtml!CTreeNode::CTreeNode+0x36: 635a321c 7406 je mshtml!CTreeNode::CTreeNode+0x3e (635a3224) [br=0] 可以看到此处将CtreeNode对应的Element对象的指针写入到了CtreeNode偏移0处。我们看一下这个对象是谁? 代码: 0:008> dd 0x0020cd58 0020cd58 02d1b1d0 00000000 ffff0000 ffffffff 0020cd68 00000000 00000000 00000000 00000000 0020cd78 00000000 00000000 00000000 00000000 0020cd88 00000000 00000000 00000000 00000000 0020cd98 00000008 00000000 00000000 00000000 0020cda8 eaa8c615 ff0c0000 002300a7 00000000 0020cdb8 ffff042f ffffffff 00000061 00000000 0020cdc8 00000000 00000000 0020cd10 0020cd68 0:008> u poi(02d1b1d0 ) mshtml!CParaElement::`vftable\’: CtreeNode中的对象确实是P对象。然后观察这个CTreeNode中的内容,第一个指针就指向了我们刚创建的P对象 因此0x0020cd38就是P对象的CtreeNode,根据分析其偏移0x40是其引用计数,现在为0x1a。 注意:此处因为多次调试,导致对象地址不同。(P对象:0x030ea7d0,其对应的CtreeNode:0x0020cd38 观察这个CTreeNode中的内容,第一个指针就指向了我们刚创建的P对象 因此0x0020cd38就是P对象的CtreeNode,其偏移0x40是其引用计数,现在为0x1a。 代码: 0:016> dd 0x0020cd38 0020cd38 030ea7d0 0020cde8 0001224d 000a0000 0020cd48 00000061 00000000 00000000 030ea740 0020cd58 030ea740 030ea710 00000062 00000000 0020cd68 00000000 030ea710 030ea710 030ea9e0 0020cd78 0000001a 0022db70 00000000 00000000 0020cd88 eaa8c7d1 ff0c0100 00237780 0020cce0 0020cd98 ffff0271 ffffffff 00000051 00000000 0020cda8 0020cdb8 0020cdf8 0020ccf0 0020cdb8 根据其释放函数来确定引用计数位置。 如果经过计算后其引用计数为0再释放这个对象的内存。 通过分析CtreeNode结构内容发现其连接方式。地址0x0020cd38的CtreeNode节点对应的的对象是CparaElement,偏移0处的地址表示节点中对象的指针。偏移0x04处为父节点指针。 代码: 0:008> dd 0x0020cd38 0020cd38 030ea7d0 0020cde8 0001224d 000a0000 0020cd48 00000061 00000000 030ea710 02d17ab8 0020cd58 02d17ab8 030ea710 00000162 00000001 0020cd68 02d17ab8 030eac20 030ea710 030ea9e0 0020cd78 0000001a 0022db70 00000000 00000000 0020cd88 eaa8c7d1 ff0c0100 00237780 0020cce0 0020cd98 ffff0271 ffffffff 00000071 00000000 0020cda8 0020cdb8 0020cd08 0020ccf0 0020cdb8 0x0020cde8节点的对象是CBodyElement 0:008> dd 0020cde8 0020cde8 002376a0 0020cb80 00012210 00080000 0020cdf8 00000571 00000008 00181038 0022b0e8 0020ce08 002376a0 030eaa10 00000262 00000004 0020ce18 030eac20 030eaa10 0020cc58 0020cba8 0020ce28 0000001a 0022dbb0 00000000 00000000 0020ce38 eaa8c7a7 ff080100 6376b210 c1800000 0020ce48 00000003 00221c80 00000000 00000000 0020ce58 00000000 00000000 00000250 00000129 0x0020cb80节点的对象是CHtmlElement 0:008> dd 0020cb80 0020cb80 030ea7a0 00181028 00026231 000b0000 0020cb90 00000161 00000002 0020cc98 00181038 0020cba0 0020ccb0 0020ccf0 00000062 00000000 0020cbb0 00181050 0020ce10 0020ce10 00181050 0020cbc0 00000010 0022dc30 00000000 00000000 0020cbd0 eaa8c71a ff0c0000 0063ffff 006c0061 0020cbe0 00700072 003a0063 004b0053 002d0059 0020cbf0 00410057 004c005b 00500052 00300043 0x00181028节点的对象是CRootElement 0:008> dd 00181028 00181028 030ea9b0 00000000 00000252 00010000 00181038 00000051 00000000 0020cb90 030eaa10 00181048 00000000 0020cc98 00000062 00000000 00181058 00000000 0020cba8 0020cba8 00000000 00181068 00000010 0022d230 00000000 00000000 00181078 ea9b0f27 ff0c0000 0000007b 00000002 00181088 0000024e 00000098 00000020 000000a2 00181098 00000020 00000119 00000028 00000127 由此可以分析出CTreeNode连接情况 CrootElement |-CHtmlElement |-CBodyElement |-CparaElement 我们继续分析在释放时究竟发生了什么事情。首先P对象会被释放,然后P对象的CtreeNode对象也会被释放并且在因为堆管理机制会导致向其内部写入数据。P对象的TreeNode结构可见0020cea8 处原本指向P对象的指针已被修改为02cbffff。 代码: 0:008> dd 0x0020cea8 0020cea8 02cbffff 00000000 ffff204d ffffffff 0020ceb8 00000051 00000000 00000000 00000000 0020cec8 00000000 00000000 00000072 00000000 0020ced8 00000000 00000000 00000000 02cb4438 0020cee8 00000001 00000000 00000000 00000000 0020cef8 eaa8a267 ff0c0100 002378f0 0020ce50 0020cf08 ffff0271 ffffffff 00000151 00000001 0020cf18 0020ce60 02cb4408 0020ce60 0020cf28 0:008> kbn # ChildEBP RetAddr Args to Child 00 0208f728 7c956f44 00000000 0022dda0 00000000 ntdll!RtlpLowFragHeapFree+0x7c 01 0208f7ec 63625834 00140000 00000000 0020cea8 ntdll!RtlFreeHeap+0x55 02 0208f7fc 6375c64e 0208f870 0208f86c 00000000 mshtml!CTreeNode::Release+0x2d … … 然后这个已经被释放的内存又会被重新使用。 代码: 0:008> g eax=63aae200 ebx=0020cea8 ecx=02cbffff edx=00000000 esi=0208dd68 edi=00000000 eip=63602713 esp=0208dd40 ebp=0208dd54 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mshtml!CTreeNode::ComputeFormats+0xb4: 63602713 e8acd50300 call mshtml!CElement::Doc (6363fcc4) 通过栈回逆分析发现是因为函数SlayoutRun::GetInnerNodeCrossingBlockBoundary中对节点的遍历导致引用了释放后的CTreeNode节点。这个函数内部先通过参数eax所指向的结CTreePos构来遍历其构成的双向链表中指向CTreeNode节点的节点。 CgeneratedContent::GetBranch此函数内部又调用函数CTreePos::GetBranch。 这个函数最终通过参数eax即为CTreePos结构来遍历。 代码: 0:008> dd eax 02cb48e8 000006f4 00000009 0020cd00 0022b180 02cb48f8 02cb4558 02ff3a70 00000002 0c000001 02cb4908 00000000 00000000 eac1a8fa ff080000 02cb4918 63aa00ce 00000000 6363c470 02cb4498 02cb4928 63601180 00000000 00000000 00000000 02cb4938 02000000 63601020 eac1a8f0 ff080100 02cb4948 63aaf438 00000002 6363c470 00237810 02cb4958 63601180 00000000 00000000 00000000 0:008> dd 02cb4558 02cb4558 000003e8 00000005 02ff36b0 0020cd00 02cb4568 00236f50 02cb48e8 00000001 001b0080 02cb4578 00000001 00000000 eac1a968 ff0c0100 02cb4588 63622630 00000001 00224320 00000000 02cb4598 002267d8 02f37328 00000000 00000000 02cb45a8 0000df70 00000000 eac1a96e ff080000 02cb45b8 63aa008c 00000000 6363c470 00237b20 02cb45c8 63628fc0 00000000 00000000 00000000 0:008> dd 00236f50 00236f50 000000e8 00000000 00000000 02cb4408 00236f60 02cb4408 02cb4558 00000001 00158320 00236f70 0020cea9 63601020 ea9cae17 ff080100 00236f80 63aaf438 00000001 6363c470 0022d020 00236f90 6363c7d0 00000000 00000000 00000000 00236fa0 02000003 63600b80 ea9cae0d ff080100 00236fb0 63aaf438 00000001 6363c470 0020d020 00236fc0 63745bf8 0020d020 63633f18 00000001 此节点中存在的问题。地址00236f70 处的值0020cea9会被用来计算成CTreeNode地址。 计算方法为: 0x0020cea8 = 0x0020cea9 & 0xFFFFFFFC 0x0020cea8 即为我们已经释放了的CTreeNode地址 然后根据调用流程分析内部会错误的解析这个CTreeNode对象的指针并企图获取其指向的CElement对象的指针并调用其虚函数。CTreePos结构00236f50中的内容是如何被修改为CtreeNode的指针的呢。即如下00236f70处。 代码: 0:008> dd 00236f50 00236f50 000000e8 00000000 00000000 02cb4408 00236f60 02cb4408 02cb4558 00000001 00158320 00236f70 0020cea9 63601020 此处分析过程比较复杂不做详细说明,经过反复调试和大量的分析后发现函数ChtmRootParseCtx::AddCollapsedWhitespace将CTreeNode指针填入了CTreePos(00236f50)结构中。然后其通过函数ChtmRootParaseCtx::InsertITreePosInChain将其链入ChtmRootParaseCtx对象中。 此时这个TreePos结构就被链入ChtmRootParaseCtx对象的链表之中。而后面的释放没有将此链表中的结构进行释放。导致uaf漏洞可以执行任意代码。 我不准备也不会在任何我发的分析文章中提供完整的利用代码,而是只分析出漏洞的原理即可。 这样一是能提高分析技巧,二是可以更好的理解被调试程序。也避免了某些同学的测试导致的损失,虽然大多数的漏洞已经有了公开的利用代码。 如果大家对这个漏洞或者这个篇分析有任何意见或建议欢迎随时指正。 谢谢。 上传的缩略图 上传的图像

版权声明:本文为saar原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/saar/archive/2013/03/19/2970222.html