• CVE-2014-0321漏洞成因分析


    1. 简介

      最近在补之前落下的想学的东西,古河之前已经在微薄里面公布了此漏洞的poc及利用思路,不过在看古河的文章前我先独立分析一下其漏洞成因,记录下自己的分析流程。

    2. 实验环境

      操作系统:Win8.1 x86 RTM

      浏览器:Internet Explorer 11 32bits (补丁打到KB2909921)

      漏洞编号:CVE-2014-0321

      微软补丁:MS14-012

    3. 漏洞分析

    3.1. 分析Crash

    3.1.1. 运行poc,查看crash

      直接运行poc,崩溃后查看状态

     

      看到crash原因是test dword ptr [ecx+24h],30000h ds:0023:41414165=???????? 处引用了无效的内存空间。查看崩溃处的上下文。

     

      在crash时的ecx来自进入函数CTreeNode::ComputeFormatsHelper时的参数1——ecx(请回顾Windows x86调用约定)。那么这个参数1到底又从哪里来呢。进一步回溯父函数:

     

      此时的ecx已经没有发生变化,再次回溯父函数:

     

      通过mov ecx,dword ptr [ebp+8]可以知道crash位置的ecx来自CInsertSpliceUndo::SetData的参数一[ebp+8]。且CInsertSpliceUndo::SetData调用CTreeNode::GetCharFormatHelper前还有别的函数调用,如下图所示:

     

    3.1.2. 释放重用的对象类型

      而CInsertSpliceUndo::SetData的父函数则为CSpliceTreeEngine::InsertSplice,通过IDA,发现第一个参数为struct CTreeNode *。因此,释放后被重用的即为struct CTreeNode *,如下图所示:

      因此,接下来在CInsertSpliceUndo::SetData处设置断点,查看进入函数时,起参数1是否已经被释放,若没有释放,则进一步跟踪释放位置。

    3.2. 跟踪调试、分析漏洞成因

    3.2.1. 调试工作准备

      开启gflags.exe的Page Heap功能。

    3.2.2. 定位对象释放位置

      设置条件断点,在进入CInsertSpliceUndo::SetData时断下函数

      bu MSHTML!CInsertSpliceUndo::SetData "r $t0 = 0;.foreach (v { k }) { .if ($spat("v", "*MSHTML!CElement::replaceNode*")) { r $t0 = 1;.break } }; .if($t0 = 0) { gc }"

      第一次断下CInsertSpliceUndo::SetData时,观察参数一:

     

      SetData第一次被断下来,此时对象还未被释放,因此对此对象的释放过程设置断点。

      bu ntdll!RtlFreeHeap ".if ( poi(esp+0xc) == 06a9efa0 ){} .else{gc}"

      通过跟踪,没有发现问题……发现被释放并占位的是另外一个CTreeNode对象,而且是在第二次调用SetData时,如下图所示

     

      由于Page Heap的作用,导致对象释放后并没有占位成功,对对象进行堆回溯,查看被释放的位置。

     

      由CFastDOM::CDocument::Trampoline_write可见JS中的document.write("");导致了对象的释放。

    3.2.3. 观察函数执行流程

      基于上面的分析,下次添加断点

    bu MSHTML!CFastDOM::CHTMLElement::Trampoline_replaceNode

    bu MSHTML!CFastDOM::CDocument::Trampoline_write

    bu MSHTML!CInsertSpliceUndo::SetData "r $t0 = 0;.foreach (v { k }) { .if ($spat("v", "*MSHTML!CElement::replaceNode*")) { r $t0 = 1;.break } }; .if($t0 = 0) { gc }"

     

      查看被断下来时的调用回溯,此时调用流程已经比较明显,通过此处的断点状态我们可以获取以下几个信息:

    1. IE内部通过MSHTML!CEventMgr::DispatchEvent进行事件监听并随后通过MSHTML!CEventMgr::Dispatch进行事件分发,分发给相应的事件监听器进行处理。
    2. IE内部通过jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk解析并处理HTML中的JS语句
    3. 在执行replaceNode语句时,IE内部触发了onerror事件,并执行其回调函数,document.write("");操作将导致某个CTreeNode对象被释放

    3.2.4. 验证对象释放并重用

      根据以上分析,下次调试设置以下断点,验证执行流程,确定被释放的对象

      bu MSHTML!CFastDOM::CHTMLElement::Trampoline_replaceNode

      bu MSHTML!CFastDOM::CDocument::Trampoline_write               //然后跟踪其对某个对象的释放过程

      根据堆栈回溯,对MSHTML!CSpliceTreeEngine::InsertSplice+0x11fa处下断点,进一步跟踪分析重用

      看看第一、二次进入SetData时的参数一(对象)的情况

    具体调试过程如下:

      bu MSHTML!CFastDOM::CHTMLElement::Trampoline_replaceNode

      bu MSHTML!CFastDOM::CDocument::Trampoline_write               //然后跟踪其对某个对象的释放过程

     

      设置快照,方便后边回溯观察对象的释放。下面根据前面Page Heap的对象释放记录,跟踪对象的释放过程

     

      条件断点:

      bu ntdll!RtlFreeHeap "r $t0 = 0;.foreach (v { k }) { .if ($spat("v", "*MSHTML!CMarkup::DestroySplayTree*")) { r $t0 = 1;.break } }; .if($t0 = 0) { gc }"

      上面这个断点设置不太好,需要消耗大量时间,还不如一步步跟踪至

      bu MSHTML!CMarkup::DestroySplayTree+0x811

      通过跟踪及快照恢复功能发现DestroySplayTree被断下来4次,其中第3次则为后边被释放重用的对象。

      对象被释放前的状态:

     

     

      可见CTreeNode节点对象的创建过程及其大小(0x60)

      对象被释放后的状态:

     

      设置如下断点,对onerror回调函数执行完毕后的情况进行跟踪

      bu MSHTML!CSpliceTreeEngine::InsertSplice+0x11fa

      跟踪至如下位置,即可发现被重用对象作为参数传递进了CInsertSpliceUndo::SetData

     

    3.2.5. 内存占位

      CTreeNode对象被document.write("");释放后,立即进行内存占位,

     

      实际大小为0x60,由于不能通过Create user mode stack trace database功能进行堆回溯,因此在Page Heap开启下无法追踪占位过程。但是通过poc,可以观察到对象已经被稳定占位。

    3.2.6. 小结

      此漏洞的效果总结来说就是对释放后重用的CTreeNode节点对象进行了稳定占位。但由于这个被释放掉的对象的可控性太低,很难在JS里面方便访问,因此利用起来有相当大的难度。

    4. 漏洞利用

      此漏洞的利用难度相当大,最近准备抽时间按照古河提供的思路尝试写EXP进行学习。请先参考资料的链接,了解此漏洞的利用方法。

    5. 参考资料

    [1]  CVE-2014-0321 - Exploiting IE11 on windows 8.1 32bits:

    http://weibo.com/p/1001603732980651659108

  • 相关阅读:
    winform 监视DataGridView的滚动条,加载数据
    SQL 自动生成行号
    SQL 删除重复的数据(多个字段判断),只保留一条数据
    C# 一个简单的 工厂模式 例子
    winform 重写TextBox的OnTextChanged方法(事件)
    winform 给文本框加载内容的时候 始终让文本框的滚动条到底(允许显示多行Multiline=True)
    winform 制定DataGridViewTextColumn列(更改DataGridView的Cell的状态很有用)
    winform 使用委托 实现多线程访问控件
    C# 一个简单的递归函数和 两个List<T> 合并
    ADO.NET  把数据库A的某表的数据内容复制到数据库B的某表内
  • 原文地址:https://www.cnblogs.com/Danny-Wei/p/3967463.html
Copyright © 2020-2023  润新知