對(duì)于Windows應(yīng)用程序和組件的漏洞,攻擊者通常都會(huì)垂涎三尺。其中,Recorded Future進(jìn)行的一項(xiàng)研究表明,2018年漏洞利用率最高的10個(gè)漏洞中有8個(gè)是Microsoft Office的漏洞。是不是覺得難以置信?實(shí)際上這一點(diǎn)也不奇怪,舉例來說,如果我們考察Microsoft Excel實(shí)例啟動(dòng)時(shí)所加載的模塊,會(huì)發(fā)現(xiàn)竟然多達(dá)91個(gè)DLL——還是那句老話,系統(tǒng)越復(fù)雜,越容易出現(xiàn)安全漏洞。此外,notepad.exe或calc.exe出現(xiàn)要想正常運(yùn)行,也需要大約30-40個(gè)DLL。
實(shí)際上,任何一個(gè)模塊中出現(xiàn)安全漏洞,都會(huì)危及Microsoft Excel自身。其中一種潛在的攻擊向量,就是向用戶提供惡意文檔,并設(shè)法讓用戶打開這些文檔。因此,對(duì)于攻擊者來說,只需找到一個(gè)含有安全漏洞的模塊,就能攻陷整個(gè)應(yīng)用程序,進(jìn)而搞定整個(gè)系統(tǒng)。這是攻擊者的最大優(yōu)勢(shì),他們擁有充足的時(shí)間,可以全面深入的研究目標(biāo),因此,通常總能找到相應(yīng)的攻擊方法。而對(duì)于系統(tǒng)安全來說,其安全性取決于系統(tǒng)中最薄弱的一個(gè)環(huán)節(jié)。
在本文中,我們通過一個(gè)具體的例子,為讀者演示如何通過比對(duì)原始程序與打過補(bǔ)丁后的程序之間的差異和逆向工程來分析導(dǎo)致Microsoft相關(guān)的漏洞的根源,并展示讓含有相關(guān)漏洞的應(yīng)用程序發(fā)生崩潰所需的具體步驟——這對(duì)于安全研究人員來說是非常有幫助的,因?yàn)閼?yīng)用程序崩潰是成功利用漏洞的重要一步。
具體來說,這里使用的漏洞是CVE-2019-0618,這是一個(gè)Windows GDI+組件的遠(yuǎn)程執(zhí)行代碼漏洞。在某些情況下,將多個(gè)普通漏洞組合在一起也足以搞定整個(gè)系統(tǒng)。鑒于此,我們認(rèn)為CVE-2019-0618很可能會(huì)成為一個(gè)被廣泛利用的漏洞,因?yàn)樗捎糜谠诟信d趣的目標(biāo)系統(tǒng)上執(zhí)行惡意軟件。有關(guān)攻擊者使用的惡意軟件免殺技術(shù)的詳細(xì)概述,請(qǐng)參閱另一篇文章。
GDI+是一種基于C/C++類的API,使應(yīng)用程序能夠在視頻顯示器和打印機(jī)上使用圖形和格式化文本。實(shí)際上,基于Microsoft Win32和Win64 API的應(yīng)用程序是無法直接訪問圖形硬件的。相反,應(yīng)用程序要想與設(shè)備驅(qū)動(dòng)程序進(jìn)行交互,必須借助于GDI+。另外,由于GDI+可用于所有基于Windows的應(yīng)用程序,使得GDI+ API已經(jīng)成為顯示W(wǎng)indows表單并管理其內(nèi)容的主要工具。
在撰寫本文時(shí),ZDI已經(jīng)公布了9個(gè)針對(duì)GDI+的漏洞,其中包括本文所研究漏洞。從公布的ZDI報(bào)告來看,本文研究的是 一個(gè)基于堆的緩沖區(qū)溢出漏洞,它能夠?qū)е逻h(yuǎn)程代碼執(zhí)行。同時(shí),根據(jù)該報(bào)告來看,該漏洞的CVSSv2得分為9.2,同時(shí)還指出,該漏洞位于DoRotatedStretchBlt方法中,這對(duì)于我們的分析工作來說是一個(gè)非常有用的線索。除此之外,它沒有公開披露與我們的調(diào)查相關(guān)的其他信息,也沒有通過針對(duì)這個(gè)漏洞的概念證明(PoC)代碼。
首先,我們想了解漏洞是如何進(jìn)行修復(fù)的?;诖耍覀兛梢酝茢喑雎┒吹拇笾虑闆r。為此,我們必須通過比較DLL打補(bǔ)丁后的版本與此前的版本的變化情況,從而縮小需要關(guān)注的代碼的范圍;具體的比對(duì)方法,請(qǐng)參考另一篇文章中的詳細(xì)介紹。簡(jiǎn)而言之,我們需要將打補(bǔ)丁后的Windows 7 x86 GdiPlus DLL與未打補(bǔ)丁的Windows 7 x86 GdiPlus DLL進(jìn)行比對(duì)。
Gdiplus.dll:未打補(bǔ)丁(左)vs已打補(bǔ)?。ㄓ遥?/div>
通過考察相關(guān)代碼在打補(bǔ)丁前后的變化情況,我們可以推斷出許多有用的信息。例如,觀察打過補(bǔ)丁的方法的數(shù)量,可以推測(cè)該特定更新的重要性。通過觀察和比較單個(gè)模塊的連續(xù)補(bǔ)丁,可以幫我們了解其開發(fā)過程,例如特定組件中出現(xiàn)的問題/漏洞的類別等。同時(shí),這種方法還可以幫助安全研究人員識(shí)別模糊測(cè)試的目標(biāo),以及哪些功能更可能存在漏洞。
利用IDA插件BinDiff,我們可以對(duì)比二進(jìn)制代碼在打補(bǔ)丁前后的變化情況,具體如下所示:
BinDiff的輸出結(jié)果:比較GdiPlus.dll在打補(bǔ)丁前后的變化情況
通過比較發(fā)現(xiàn),DoRotatedStretchBlt方法確實(shí)發(fā)生了一些變化,并且這里還具有較高的置信度。下面,讓我們?cè)敿?xì)看看圈起來部分的變化情況。
BinDiff的輸出結(jié)果:gdiplus!DoRotatedStretchBlt方法,已打補(bǔ)丁與未打補(bǔ)丁的情況
BinDiff插件的顏色的含義:
綠色節(jié)點(diǎn)表示在兩個(gè)可執(zhí)行文件中具有相同指令的基本塊;
紅色節(jié)點(diǎn)表示比較算法無法找到等價(jià)物的基本塊;
黃色節(jié)點(diǎn)表示算法可以找到等價(jià)物的節(jié)點(diǎn),但是在不同版本中,某些指令已經(jīng)發(fā)生了變化。
在我們的例子中,我們可以看到打過補(bǔ)丁的DLL中有一些紅色基本塊,所以,我們可以使用IDA Hex-Rays反匯編程序來進(jìn)一步查看實(shí)際的差異。
IDA的輸出結(jié)果:GdiPlus!DoRotatedStretchBlt方法,已打補(bǔ)丁的版本(左)vs未打補(bǔ)丁的版本(右)
通過觀察這個(gè)補(bǔ)丁,可以看出它處理的是一種典型的安全問題——使用memcpy時(shí)沒有進(jìn)行適當(dāng)邊界檢查。該補(bǔ)丁似乎是用于處置Size參數(shù)的不同值的,以避免發(fā)生溢出。奇怪的是,并非所有的情況都得到了相應(yīng)的處理。這是因?yàn)?,與Windows補(bǔ)丁程序的情況一樣,補(bǔ)丁程序不會(huì)出現(xiàn)在漏洞所在的確切位置。進(jìn)一步的研究表明,該補(bǔ)丁程序的另一部分位于StretchBLT記錄的處理程序中:
IDA的輸出結(jié)果:GdiPlus!_bHandleStretchBlt方法,已打補(bǔ)丁的版本(左)vs未打補(bǔ)丁的版本(右)
我們注意到,除了GdiPlus!pfnIsValidEnhMetaRecordOffExt方法(該方法也存在于未打補(bǔ)丁的程序庫中)之外,該補(bǔ)丁程序中還含有另一個(gè)驗(yàn)證方法,即GdiPlus!bValidateAndExpandBuffers方法。后者的作用是清理Src緩沖區(qū)(pjBits)和Ubytes(v21)參數(shù)。然后,如果滿足某些條件的話,DoStretchBlt將調(diào)用存在漏洞的函數(shù)。
IDA的輸出結(jié)果:GdiPlus!DoStretchBlt方法
如果針對(duì)hdc_flag的AND運(yùn)算的結(jié)果為true,則調(diào)用相關(guān)的函數(shù)。變量hdc存放的是一個(gè)Windows設(shè)備上下文的引用。而所謂的設(shè)備上下文其實(shí)就是一種結(jié)構(gòu)體,用于定義一組圖形對(duì)象及其相關(guān)屬性,以及影響輸出的圖形模式。在我們的例子中,圖形對(duì)象包含一個(gè)用于剪切、噴涂和繪制操作的區(qū)域。
總之,要利用這個(gè)漏洞,需要控制DoRotatedStretchBlt方法的Src和Ubytes參數(shù)。所以,我們首先必須設(shè)法滿足這一要求。了解如何訪問該函數(shù)的最佳方式,就是使用IDA的調(diào)用圖功能。
針對(duì)gdiplus!DoRotatedStretchBlt函數(shù)的調(diào)用圖
如您所見,我們可以通過調(diào)用不同的處理程序(例如,bHandlePlgBlt或bHandleSetDIBitsToDev)來執(zhí)行DoRotatedStretchBlt操作。這些似乎是不同EMF記錄的處理程序。我們的問題是如何處理不同的EMF記錄?
了解EMF格式
增強(qiáng)型圖元文件格式(Enhanced metafile format,EMF)是一種用于存儲(chǔ)圖形圖像的可移植文件格式。EMF元文件中存放的是順序記錄,這些記錄經(jīng)過解析和處理后,可以在任何輸出設(shè)備上顯示文件中存儲(chǔ)的圖像。這個(gè)程序庫必須能夠以某種方式解釋這些記錄的類型,這樣,當(dāng)處理EMF文件時(shí),就能為其提供正確的處理程序。在二進(jìn)制文件中進(jìn)行快速搜索,就能找到_pdofnDrawingOrders方法:
GdiPlus!pdofnDrawingOrders存放每個(gè)記錄處理程序的引用
我們可以發(fā)現(xiàn),這里列舉了所有受支持的記錄,包括我們的bHandleStretchBlt記錄處理程序。下面,我們將設(shè)法觸發(fā)DrawingOrder操作。
IDA:GdiPlus!bParseWin32Metafile方法
GdiPlus!bParseWin32Metafile方法將解析WMF文件頭部,如果其中存在有效的EMF記錄,它將通過從_pdofnDrawingOrders調(diào)用正確的處理程序來解析它們。隨后,該解析方法會(huì)被GdipConvertEmfToWmFBits調(diào)用。除此之外,我們沒有發(fā)現(xiàn)針對(duì)該方法的其他交叉引用,這表明必定存在一些外部API。通過閱讀Win GDI+文檔,發(fā)現(xiàn)它似乎不推薦使用GdipConvertEmfToWmFBits,所以我們?cè)贛etafile類中為其提供了一個(gè)封裝器,即Metafile::EmfToWmfBits.。目前來看,我們似乎勝利在望了,至少已經(jīng)看到了曙光。
讓我們先暫停前進(jìn)的步伐,回顧一下前面講過的內(nèi)容:
· 易受攻擊的方法gdiplus!DoRotatedStretchBlt是由gdiplus!DoStretchBlt調(diào)用的,而后者是用于處理EMF_STRETCHBLT記錄類型的。
· 如果為設(shè)備句柄設(shè)置了某個(gè)標(biāo)志(值4),則會(huì)調(diào)用易受攻擊的方法。
· 我們最終可以通過觸發(fā)Metafile::EmfToWmfBits操作來調(diào)用易受攻擊的方法,其中一個(gè)參數(shù)是包含EMF_STRETCHBLT記錄的EMF文件。
現(xiàn)在,我們已經(jīng)通過逆向分析獲得了足夠的信息,所以逆向工作可以告一段落了;接下來,我們要做的事情是,通過模糊測(cè)試找到一個(gè)能夠?qū)е乱资芄舻姆椒òl(fā)生崩潰的EMF文件,同時(shí)也希望能找到其他讓人感興趣的東西。
模糊測(cè)試的考慮事項(xiàng)
1.選擇fuzzer
對(duì)于Windows二進(jìn)制文件來說,當(dāng)前最先進(jìn)的模糊測(cè)試框架是WinAFL和Peach Fuzzing Framework。在本文中,我們將使用WinAFL,因?yàn)樗且环N基于突變的、覆蓋率反饋驅(qū)動(dòng)型的模糊引擎。用于Linux應(yīng)用程序的AFL的詳細(xì)介紹,請(qǐng)參閱這篇文章。對(duì)于插樁技術(shù),我們采用的是DynamoRio,這是一種運(yùn)行時(shí)檢測(cè)框架,能夠在塊覆蓋率模式下運(yùn)行。
2.創(chuàng)建測(cè)試工具
現(xiàn)在,必須創(chuàng)建一個(gè)使用易受攻擊的GdiPlus.dll庫的Windows GUI應(yīng)用程序。最終的測(cè)試工具看起來像這樣:
用于測(cè)試GdiPlus.dll庫的測(cè)試工具
首先,我們會(huì)讀取作為參數(shù)提供的EMF文件。然后,我們將啟動(dòng)一個(gè)EmfToWmfBits操作(將第136行),這將有望觸發(fā)該漏洞。接著,觸發(fā)針對(duì)EMF文件的Metafile::EmfToWmfBits操作后,通過釋放使用過的組件并關(guān)閉打開的文件來清理環(huán)境。
3.樣本的生成
為了得到一個(gè)包含EMR_STRETCHBLT記錄的EMF文件,我們專門研究了WinGDI和GDI+文檔,并找到了一個(gè)簡(jiǎn)單的生成器,具體如下所示:
用于生成包含EMR_STRETCHBLT記錄的EMF文件的生成器
我們還在初始語料庫中的EMF文件中通過了其他記錄類型以提高覆蓋率,以增加導(dǎo)致其崩潰的機(jī)會(huì)。
4.語料庫最小化與覆蓋率
對(duì)模糊測(cè)試進(jìn)行優(yōu)化時(shí),其中一個(gè)重要步驟就是語料庫的最小化。該過程將刪除無法顯著改善代碼覆蓋率的樣本,以防止在這些樣本上浪費(fèi)CPU周期。利用winafl-cmin.py腳本(它是WinAFL存儲(chǔ)庫的一部分)處理最初的32個(gè)樣本的EMF語料庫后,最終找到了10個(gè)重要的樣本:
通過winafl-cmin.py腳本實(shí)現(xiàn)語料庫最小化
這里要做的是,在DynamoRio的運(yùn)行時(shí)檢測(cè)模式下,通過測(cè)試工具運(yùn)行所有初始測(cè)試用例,同時(shí),在每次運(yùn)行時(shí)捕獲感興趣的庫中已到達(dá)的基本塊。
使用IDA的Lighthouse插件,可以查看最終語料庫是否具有良好的覆蓋率:
IDA Lighthouse插件:GdiPlus.dll的代碼覆蓋率
如您所見,我們的目標(biāo)GdiPlus!GdipEmfToWmfBits具有很好的覆蓋率,同時(shí),程序庫的大部分代碼也具有很好的覆蓋率。
5.最終結(jié)果
經(jīng)過1天21小時(shí)的測(cè)試后,我們找到了80多個(gè)庫崩潰:
WinAFL的運(yùn)行結(jié)果
使用BugID對(duì)它們進(jìn)行分類,發(fā)現(xiàn)有多個(gè)樣本能導(dǎo)致我們感興趣的方法發(fā)生崩潰:
BugID的測(cè)試報(bào)告:GdiPlus!DoRotatedStretchBlt中可能存在RCE漏洞
使用崩潰樣本啟動(dòng)WinDbg實(shí)例,結(jié)果:
WinDbg的輸出:通過模糊測(cè)試獲得的示例,觸發(fā)與CVE-2019-0618相對(duì)應(yīng)的崩潰;綠框內(nèi)是stacktrace數(shù)據(jù),紅框內(nèi)是崩潰分析數(shù)據(jù)
通過類似的方式,我們獲得了與CVE-2019-0614、CVE-2019-0618和CVE-2019-0619相關(guān)的庫崩潰。此外,我們?cè)谀:郎y(cè)試過程中發(fā)現(xiàn)了一些其他方面的庫漏洞,并將這些漏洞也提交給了微軟。