談談Office Moniker類漏洞和公式編輯器類漏洞
在近幾年出現的諸多office漏洞中,有兩類漏洞是很值得談談的,第一類是Moniker導致的邏輯漏洞,第二類是公式編輯器漏洞。
對于第一類漏洞,其重點在于攻擊者和微軟安全團隊之間的攻防過程,了解這類漏洞攻防過程對威脅追蹤與研究較有益處:
- 第一回合:CVE-2017-0199,用URL Moniker加載遠程HTA文件實現遠程代碼執行;
- 第二回合:CVE-2017-8570,用CompositeMoniker、FileMoniker、NewMoniker、scriptletfile實現遠程代碼執行;
- 第三回合:CVE-2017-8759,用office文檔加載.Net組件漏洞,實現遠程代碼執行;
- 第四回合:CVE-2018-8174,用office文檔加載IE VBScript組件漏洞,實現遠程代碼執行;
- 第五回合:CVE-2020-0674,用office文檔加載IE JavaScript組件漏洞,實現遠程代碼執行。
對于第二類漏洞,其難點在于對相似漏洞之間的區分。從CVE-2017-11882開始,到CVE-2018-0802,再到CVE-2018-0798,三個都是非常相似的漏洞,在靜態層面不容易區分,本文將分享一個在動態層面區分它們的方法。
下面跟隨筆者一起來看一下這兩類漏洞。
Moniker類漏洞
第一回合:CVE-2017-0199
2017年4月7日,著名office漏洞研究員李海飛發布了一篇在野0day攻擊預警,首度披露了CVE-2017-0199漏洞的在野攻擊。隨后,2017年4月11日和12日,FireEye連發兩篇文章,披露了他們捕獲到的CVE-2017-0199漏洞樣本細節。后續的披露表明這幾篇文章中披露的漏洞是一種借助URL Moniker特性加載遠程hta文件的新型漏洞,這是一個由于開發者對office文件加載機制設計不合理導致的邏輯漏洞,且要求觸發環境安裝IE10/IE11。漏洞觸發過程不需要用戶交互,但在觸發的過程中會彈出一個對話框,不點擊或者點擊任意該對話框的按鈕都不影響執行過程,對話框樣式如下:

該漏洞的發現者之一李海飛曾經在Syscan360 2017會議上做過題為《Moniker Magic: Running Scripts Directly in Microsoft Office》的演講,里面詳細介紹了CVE-2017-0199的細節,包括:
- 微軟在CVE-2017-0199的補丁中修復了兩個漏洞,分別是被在野利用的RTF URL Moniker加載遠程HTA文件的遠程代碼執行漏洞,和李海飛獨立發現的PPSX Script Moniker遠程代碼執行漏洞;
- office安全團隊在這兩個漏洞的基礎上設計了一類針對Moniker的黑名單機制,禁用了一些他們覺得不安全的Moniker。
Moniker本身是office的一個特性,可以用來鏈接一些本地或遠程對象,其本身不屬于漏洞,漏洞發生在office軟件對遠程鏈接的文件的執行策略上。譬如,如果遠程加載的是一個Excel文件,直接打開沒問題;但如果加載的是HTA文件和Script這類腳本文件時,直接執行就存在問題了,導致了漏洞。
第二回合:CVE-2017-8570
在對CVE-2017-0199補丁的研究過程中,李海飛發現(上面也已經提到):
office安全團隊在這CVE-2017-0199的補丁中設計了一類針對Moniker的黑名單機制,禁用了一些他們覺得不安全的Moniker。
于是他開始尋找那些還沒有被禁用的Moniker,嘗試用那些沒有被禁用的Moniker構造出另一個邏輯漏洞,結果確實找到一個,即CVE-2017-8570。
在CVE-2017-0199中,用到的Moniker是下面這兩個:
3050F4D8-98B5-11CF-BB82-00AA00BDCE0B // htafile06290BD3-48AA-11D2-8432-006008C3FBFC // scriptlet(or ScriptMoniker)
而在CVE-2017-8570中,用到的Moniker是下面這幾個:
00000309-0000-0000-C000-000000000046 // CompositeMoniker00000303-0000-0000-C000-000000000046 // FileMonikerECABAFC6-7F19-11D2-978E-0000F8757E2A // NewMoniker06290BD2-48AA-11D2-8432-006008C3FBFC // scriptletfile(or ScripletFactory)
可以看到CVE-2017-8570利用未被加入黑名單的Moniker繞過了CVE-2017-0199的補丁。
不過,許多分析過CVE-2017-8570的讀者可能會觀察到一個奇怪的現象:漏洞中觸發時script腳本中的代碼會被執行兩次。這是為什么呢?原來,在這個漏洞的觸發邏輯中,會涉及到wwlib.dll庫中的一個函數調用,該函數內部會順序調用ole32!CDefLink::BindToSource和ole32!CDefLink::Update兩個函數,如下(以office 2010為例):

而這兩個函數最終都會調用kernel32!CreateProcessW創建進程,所以script腳本中的代碼會被執行兩次。其中ole32!CDefLink::BindToSource創建進程的棧回溯如下:
0:000> k 50ChildEBP RetAddr 0013a5b4 729cd2f5 kernel32!CreateProcessW0013a63c 729cd5f7 wshom!CreateNewProcessW+0x6f0013a69c 76da3e75 wshom!CWshShell::Exec+0x19a0013a6bc 76da3cef OLEAUT32!DispCallFunc+0x1650013a74c 729d0267 OLEAUT32!CTypeInfo2::Invoke+0x23f...cut...0013ae9c 7705b5dc comsvcs!CNewMoniker::BindToObject+0x14f0013aed0 770c3cc6 ole32!CCompositeMoniker::BindToObject+0x105 [d:\w7rtm\com\ole32\com\moniker2\ccompmon.cxx @ 1104]0013af3c 68ee87ce ole32!CDefLink::BindToSource+0x1bf [d:\w7rtm\com\ole32\ole232\stdimpl\deflink.cpp @ 4637]0013af80 68a61429 wwlib!wdGetApplicationObject+0x69230 // 第一處調用0013b010 68a23b2c wwlib!DllGetLCID+0x4753b3...cut...
而ole32!CDefLink::Update創建進程的棧回溯如下:
0:000> k 50ChildEBP RetAddr 0013a57c 729cd2f5 kernel32!CreateProcessW0013a604 729cd5f7 wshom!CreateNewProcessW+0x6f0013a664 76da3e75 wshom!CWshShell::Exec+0x19a0013a684 76da3cef OLEAUT32!DispCallFunc+0x1650013a714 729d0267 OLEAUT32!CTypeInfo2::Invoke+0x23f...cut...0013ae68 7705b5dc comsvcs!CNewMoniker::BindToObject+0x14f0013ae9c 770c3c55 ole32!CCompositeMoniker::BindToObject+0x105 [d:\w7rtm\com\ole32\com\moniker2\ccompmon.cxx @ 1104]0013af08 7710f7ee ole32!CDefLink::BindToSource+0x14e [d:\w7rtm\com\ole32\ole232\stdimpl\deflink.cpp @ 4611]0013af30 7710f42a ole32!CDefLink::Update+0x62 [d:\w7rtm\com\ole32\ole232\stdimpl\deflink.cpp @ 5347]0013af44 68ee8830 ole32!CDefLink::Update+0x33 [d:\w7rtm\com\ole32\ole232\stdimpl\deflink.cpp @ 2695]0013af80 68a61429 wwlib!wdGetApplicationObject+0x69292 // 第二處調用 0013b010 68a23b2c wwlib!DllGetLCID+0x4753b3...cut...
第三回合:CVE-2017-8759
在CVE-2017-8570漏洞被修復后,累計有如下這些Moniker被加入黑名單:
3050F4D8-98B5-11CF-BB82-00AA00BDCE0B // htafile06290BD3-48AA-11D2-8432-006008C3FBFC // scriptlet(or ScriptMoniker)00000309-0000-0000-C000-000000000046 // CompositeMoniker00000303-0000-0000-C000-000000000046 // FileMonikerECABAFC6-7F19-11D2-978E-0000F8757E2A // NewMoniker06290BD2-48AA-11D2-8432-006008C3FBFC // scriptletfile(or ScripletFactory)
在前面幾個Moniker不能使用之后,攻擊者又注意到了下面這個Moniker:
ecabb0c7-7f19-11d2-978e-0000f8757e2a // SOAPMoniker
SOAP Moniker可以用來加載一個遠程的SOAP配置文件,當Word進程遠程加載這個配置文件時,.Net組件會被加載用來解析對應的配置文件,并按照配置自動生成一個C#文件,再自動將該C#文件編譯得到一個動態鏈接庫并執行。攻擊者借助.Net SOAP WSDL模塊中的一個代碼注入漏洞(CVE-2015-8759),將惡意腳本代碼注入到了待編譯的C#文件中,從而讓編譯得到的動態鏈接庫包含惡意代碼并自動執行。
從CVE-2017-8759開始,攻擊者開始借助office組件與其他Windows組件之間的交互進行攻擊。.Net的漏洞本身不屬于office的范圍,卻可以借助office文檔進行觸發,這種攻擊方式當時給筆者留下了深刻的印象。
第四回合:CVE-2018-8174
CVE-2017-8759被修復后,Moniker黑名單又得到了更新:
3050F4D8-98B5-11CF-BB82-00AA00BDCE0B // htafile06290BD3-48AA-11D2-8432-006008C3FBFC // scriptlet(or ScriptMoniker)00000309-0000-0000-C000-000000000046 // CompositeMoniker00000303-0000-0000-C000-000000000046 // FileMonikerECABAFC6-7F19-11D2-978E-0000F8757E2A // NewMoniker06290BD2-48AA-11D2-8432-006008C3FBFC // scriptletfile(or ScripletFactory)ecabb0c7-7f19-11d2-978e-0000f8757e2a // SOAPMoniker
在上面這些Moniker都不可用之后,攻擊者又想出了一種新的攻擊方式:借助URL Moniker去加載遠程html文件,這樣就可以借助office加載IE漏洞。攻擊者首先用URL Moniker+CVE-2014-6332的組合試了一下該方案的可行性,筆者追溯到的這方面的最早樣本為2018年1月17日的下面這個文件(以及相關文件):
// CVE-2014-6332Document MD5: A9D3F7A1ACD624DE705CF27EC699B6B6URL Moniker: hxxp://s.dropcanvas[.]com/1000000/940000/939574/akw.htmlakw.html MD5: C40A128AE7AEFFA3C1720A516A99BBDF
到了2018年4月,攻擊者終于按捺不住了,借助URL Moniker+IE VBScript 0day的方式對特定目標進行了攻擊,這次攻擊所用漏洞就是著名的CVE-2018-8174,相關樣本如下:
// CVE-2018-8174Document MD5: b48ddad351dd16e4b24f3909c53c8901URL Moniker: hxxp://autosoundcheckers[.]com/s2/search[.]php?who=7search.htm MD5: 15eafc24416cbf4cfe323e9c271e71e7
CVE-2018-8174出現后,微軟安全團隊并未直接將office加載VBScript腳本的功能進行限制。隨后,在2018年7月,攻擊者又借助另一個IE VBScript 0day(CVE-2018-8173),用相同的方式實施了攻擊。
這下微軟不淡定了,趕緊對Office加載VBScript腳本進行了限制。
第五回合:CVE-2020-0674
故事到這里就結束了嗎?當然沒有。此時,微軟依然沒有限制office加載JavaScript腳本,所以IE瀏覽器的兩個JavaScript引擎:JScript和JScript9依然可以通過此種方式進行攻擊。
其一,據筆者所知,在2018年的天府杯上,針對office項目的攻擊采用了URL Moniker + IE JScript9 0day的組合。
其二,2019年-2020年,由于幾個JScript漏洞被相繼披露,陸續有APT攻擊組織使用URL Moniker + JScript 1day的方式實施攻擊,相關樣本如下:
// CVE-2020-0674Document MD5: 90403dfafa3c573c49aa52c5fe511169URL Moniker: hxxp://tsinghua.gov-mil[.]cn/images/A96961AA/36604/1836/65449576/ab8feeeab8feee MD5: 1892D293030E81D0D1D777CB79A0FDBE // CVE-2020-0968Document MD5: 60981545a5007e5c28c8275d5f51d8f0URL Moniker: hxxp://94.156.174[.]7/up/a1a.htma1a.htm MD5: 293916af3a30b3d7a0dc2949115859a6
于是微軟在高版本office中(office2016及以上版本)也加入了對JScript9腳本和JScript腳本的加載限制。
至此,攻擊者針對Moniker的所有嘗試都被微軟進行了封堵,此后未觀察到針對Moniker的新攻擊方式。
公式編輯器漏洞
2017年11月補丁日,國外安全公司_embedi發表了一篇《SKELETON IN THE CLOSET: MS Office vulnerability you didn’t know about》詳細描述了他們發現office公式編輯器漏洞CVE-2017-11882的整個過程(筆者發現這家公司的官網已經掛了…)。
屬于office公式編輯器漏洞的時代至此開啟。
由于組件源碼的丟失,微軟的補丁開發人員花了較長時間來修復這一漏洞,并且以一種近乎炫技的方式,直接在二進制層面對程序作了修補,在沒有重新編譯源碼的情況下修復了漏洞,并添加了ASLR支持。
然而,一時激起千層浪,CVE-2017-11882出現后,廣大安全研究員蜂擁而至,都開始關注office公式編輯器這一組件,這直接導致微軟在2018年1月的更新中砍掉了公式編輯器組件。
在第二次修復的諸多office公式編輯器漏洞中,有兩個漏洞比較值得注意,這兩個漏洞分別為CVE-2018-0802和CVE-2018-0798,三個漏洞并稱為office公式編輯器漏洞領域的“三駕馬車”,
由于筆者經常看到分析人員對這三個漏洞的樣本進行誤判,所以這里分享一種在動態層面區分這三個漏洞的方法。
首先跟隨筆者來了解一下這三個漏洞的具體成因,下文中的匯編代碼基于以下公式編輯器組件:
eqnedt32.exe 2000.11.9.0
在office中,公式編輯器的數據被存儲在一個OLE文件的“Equation Native”流中,三個公式編輯器漏洞都是在處理這個流的數據時出現的問題。

CVE-2017-11882
首先來看一下CVE-2017-11882。
該漏洞的成因為:在讀入“Equation Native”流中的Font Name Record數據時,在將Name拷貝到某個局部變量的時候沒有對Name的長度做校驗,從而造成棧緩沖區溢出,漏洞發生點如下圖所示:

從下圖可以看出,函數給SrcStr變量分配的大小是0x24個字節,Name長度超過該大小就會造成棧溢出。

CVE-2017-11882的觸發邏輯如下所示:

CVE-2018-0802
再來看一下CVE-2018-0802。
該漏洞的成因為:在將“Equation Native”流中的Font Name Record數據拷貝到一個LOGFONT結構體(位于棧上)內的lfFaceName成員(它是一個以空結尾的char型字符串,最大長度為0x20,其中包含終止符NULL),沒有對Name的長度做校驗,從而造成棧緩沖區溢出,漏洞發生點如下圖所示:

CVE-2018-0802漏洞的觸發路徑和CVE-2017-11882有很大的重疊,下圖可以做一個直觀的比對:

由于某些限制,CVE-2018-0802在未打CVE-2017-11882補丁的版本上只會造成crash,但在打了補丁的版本上可以實現遠程代碼執行。
CVE-2018-0798
最后看一下CVE-2018-0798。
該漏洞的成因為:在讀入“Equation Native”流中的Matrix Record數據時,存在一處while循環內的數據讀取操作,由于未對Matrix的行和列兩個參數進行校驗,從而使攻擊者可以控制由此計算得到的拷貝長度,導致棧緩沖區溢出:

上述匯編片段描述了一個while循環,反匯編成偽代碼如下,攻擊者可以控制偽碼中v2的大小,從而導致了數據讀寫越界:

上述代碼位于sub_443F6C函數內,所以理論上只要調用sub_443F6C函數的地方均存在CVE-2018-0798漏洞。作為與之前兩個漏洞的對比,在之前兩個漏洞的基礎上加入CVE-2018-0798的觸發路徑如下:

動態區分三個公式編輯器漏洞
以上筆者已經介紹了三個公式編輯器漏洞的成因,借助上述知識,很容易在調試器中確認特定樣本使用的漏洞,判定方式如下:
// CVE-2017-11882.text:00411655 C1 E9 02 shr ecx, 2 // 獲取此偏移處的ecx值,若ecx的值位于(0x20, 0x94]區間,即為CVE-2017-11882.text:00411658 F3 A5 rep movsd.text:0041165A 8B C8 mov ecx, eax.text:0041165C 83 E1 03 and ecx, 3 // CVE-2018-0802.text:00421E5B C1 E9 02 shr ecx, 2 // 獲取此偏移處的ecx值,若ecx的值大于0x94,即為CVE-2018-0802.text:00421E5E F3 A5 rep movsd.text:00421E60 8B C8 mov ecx, eax.text:00421E62 83 E1 03 and ecx, 3.text:00421E65 F3 A4 rep movsb // CVE-2018-0798.text:00443F79 8D 04 45 02 00 00 00 lea eax, ds:2[eax*2].text:00443F80 83 C0 07 add eax, 7.text:00443F83 C1 F8 03 sar eax, 3.text:00443F86 66 89 45 08 mov [ebp+arg_0], ax // 獲取此偏移處的eax值,若eax的值大于4,即為CVE-2018-0798
有些樣本會同時滿足上述兩個或三個條件,因為這些樣本中內嵌多個公式編輯器漏洞利用。