Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
115 lines (86 sloc) 8.33 KB

#CVE-2013-1347 Microsoft Internet Explorer CGenericElement Object Use After Free Vulnerability Analysis

1. 前言

最近野外出现了一个针对Internet Explorer 8浏览器0 day漏洞的攻击。在获得样本后对其进行了深入分析,分析后确实感触颇多。首先虽然国外及国内的一些文章在较快的时间给出了一些对此漏洞的分析。但是他们所做的分析结果是片面,低级的。并没有深入分析漏洞的成因,也没有弄清楚漏洞的原理(只是知道是个uaf漏洞,当然分析明白的不愿放出来的除外)。这样的浅显的漏洞报告毫无意义(通过漏洞的命名也可说明,多数研究者根本就不了解漏洞的原理)。
说明一下本人不准备在报告中提供完整的具有攻击性的poc。以避免因某些同学测试导致的损失,在此我们只关注技术本身(其实exploit已经公开)。

2. 分析

首先明确一点此漏洞与CGenericElement对象毫无关系,唯一的关系可能就是开始的攻击者恰巧通过CGenericElement对象来实现Exploit。为了证明这个问题,后面的分析试验通过另一个元素"a",即为CAnchorElement对象来实现Exploit。

  • javascript对DOM操作的流程: 首先如果javascript代码块中存在修改DOM树结构的代码时(如:appendChild、innerHTML等),每条修改DOM的语句虽然已经在执行时实际的修改了DOM树的结构,但新创建的DOM元素或被修改的元素并未马上被渲染。而是当javascript代码块结束后再根据DOM树来统一渲染(提高效率)。
  • offsetParent语句: offsetParent语句返回的是,离指定元素最近的已被定为的父元素指针。如果元素没有被CSS定位则返回父元素或body元素的指针(对象的引用)。但是这里有一个没有提到的特性,我们稍后详细描述。就是因为这个特性导致了这个漏洞。

理解了这两个机制后,我们接下来详细分析这个漏洞。通过对比分析发现当javascript代码块中存在修改DOM树的代码时,并且存在未被渲染的DOM元素时(新创建的DOM元素)如果调用了offsetParent代码(可根据任意元素来引用)。将导致未被渲染过的元素属性被修改为已渲染。这样在修改其DOM节点时(如通过innerHTML属性来释放其所有子元素)也不会通过CtreeNode::ComputeFormats函数重新计算节点格式。正常情况下会调用CtreeNode::ComputeFormats函数重新计算节点格式函数重新计算节点格式。而没有重新计算的话,会导致浏览器并不知道元素释放情况,也未更新元素节点关系。处理过程对比如下。

not_offsetParent_Release

(正常的流程会调用ComputeFormats函数,不会导致问题。)

offsetParent_Release

(错误的调用,导致漏洞。)

那么为什么调用了offsetParent语句就不调用CtreeNode::ComputeFormats函数了呢?首先我们先分析一下正常的处理流程。正常的处理流程是,当通过innerHTML释放了未被渲染的元素的所有子元素后。其会通过以下流程的函数调用最终调用到CtreeNode::ComputeFormats函数对CTreeNode节点进行重新计算。

GS_PropEnum
	|- CElement::put_innerHTML
		|- Celement::InjectCompatBSTR
			|- CElement_InjectInternal
				|- CtreeNode::IsEditable
					|- CtreeNode::GetCharFormat				⇐ 判断属性
						|- CtreeNode::GetCHarFormatHelper
							|- CtreeNode::GetCharFormatIndexHelper
								|- CtreeNode::ComputeFormatsHelper
									|- CtreeNode::ComputeFormats 

注意:在调用到CtreeNode::GetCharFormat函数时。CtreeNode::GetCharFormat函数内部会判断当前元素对应的TreeNode节点的属性,既为偏移0x0c处的word长度的属性值。此属性经分析发现应为表示节点渲染情况的属性。

cmp_CTreeNode_0x0c


当此值小于0时(正常流程时值为0xffff)则进行跳转,会继续调用CtreeNode::GetCHarFormatHelper函数,进入重新计算CTreeNode格式的后续处理流程。而当代码中存在offsetParent代码时,此处CTreeNode偏移0x0c处的属性不为0xffff而为0x0001(表示已经渲染,无需重新计算CTreeNode)因此则不进入CtreeNode::GetCHarFormatHelper函数也无法调用到其后续函数CtreeNode::ComputeFormats对CTreeNode进行重新计算。最终导致了问题。存在问题的调用流程如下。
GS_PropEnum
	|- CElement::put_innerHTML
		|- Celement::InjectCompatBSTR
			|- CElement_InjectInternal
				|- CtreeNode::IsEditable
					|- CtreeNode::GetCharFormat				⇐ 判断属性

我们看一下CTreeNode属性的不同

not_offsetParent_CTree not_offsetParent_CTree_Data

(正常的未渲染的CTreeNode节点)

offsetParent_CTree offsetParent_CTree_Data

(错误的未渲染的CTreeNode节点,导致了漏洞)

并且会将所有的CTreeNode,即使是未渲染过的节点的属性全部修改为0x0001。

offsetParent_All_CTree offsetParent_All_CTree_Data

(错误的将所有未渲染的CTreeNode节点属性全部修改,导致了漏洞)

最终当javascript语句块执行完毕后,浏览器会重新渲染构建好的DOM树。而因为之前在释放节点时没有调用函数CtreeNode::ComputeFormats重新计算CTreeNode节点,导致了浏览器仍然企图渲染释放后的元素。最终引用了释放后的元素,成功利用后可以导致任意代码执行。

poc

(成功利用后可导致任意代码执行。)

3. 总结

最终我们明白了这个漏洞的根本原因在于offsetParent语句会将未渲染的CTreeNode节点通过“个性”的方式进行处理,并最终修改其属性未渲染后。这样在后续对DOM树进行修改后就导致了浏览器并没有正确的处理DOM树的节点之间的关系,最终导致了uaf漏洞。

分析完这个漏洞后并对比了一下国内外的一些分析报告不难发现一个奇怪的问题。多数安全研究者竟然错误的认为这个漏洞仍是CGenericElement对象引起的。不难看出大家并没有深入的分析这个漏洞的根本成因(虽然我分析的也不够深入)。连漏洞的名字也错误的引导大家以为是CGenericElement对象的问题(在我修改的POC中可以清楚的看到根本没有存在任何CGenericElement对象,而是使用CAnchorElement对象),而其实漏洞的原因在另一个地方。并且多数的分析文章也只是停留在了最终的对象释放后重用的位置,并未深入分析到漏洞的根本原因,个人认为这种浅显、低级并且没有说明根本原因也未解决任何技术问题的报告简直就不应该出现。这完全是不负责任的表现(如果你尽力的去分析了,是因为你的技术水平不够导致理解错误,这种情况可以理解。大家都是从不断的错误中吸取教训而成长起来的。但是在你技术能力范围之内的分析仍浅尝即止就是态度的问题了)。本篇文章没有针对任何人的意思,而是单纯的从技术角度来看待某些报告的价值。

最后希望本文对你有所帮助。如发现文章存有逻辑、语义或错别字等错误,欢迎随时留言指正,定当在第一时间改正错误。同时也欢迎对某处理解存在问题的同学随时留言提问,也一定在第一时间予以解答。