對于Windows應(yīng)用程序和組件的漏洞,攻擊者通常都會垂涎三尺。其中,Recorded Future進行的一項研究表明,2018年漏洞利用率最高的10個漏洞中有8個是Microsoft Office的漏洞。是不是覺得難以置信?實際上這一點也不奇怪,舉例來說,如果我們考察Microsoft Excel實例啟動時所加載的模塊,會發(fā)現(xiàn)竟然多達91個DLL——還是那句老話,系統(tǒng)越復(fù)雜,越容易出現(xiàn)安全漏洞。此外,notepad.exe或calc.exe出現(xiàn)要想正常運行,也需要大約30-40個DLL。
實際上,任何一個模塊中出現(xiàn)安全漏洞,都會危及Microsoft Excel自身。其中一種潛在的攻擊向量,就是向用戶提供惡意文檔,并設(shè)法讓用戶打開這些文檔。因此,對于攻擊者來說,只需找到一個含有安全漏洞的模塊,就能攻陷整個應(yīng)用程序,進而搞定整個系統(tǒng)。這是攻擊者的最大優(yōu)勢,他們擁有充足的時間,可以全面深入的研究目標,因此,通??偰苷业较鄳?yīng)的攻擊方法。而對于系統(tǒng)安全來說,其安全性取決于系統(tǒng)中最薄弱的一個環(huán)節(jié)。
在本文中,我們通過一個具體的例子,為讀者演示如何通過比對原始程序與打過補丁后的程序之間的差異和逆向工程來分析導(dǎo)致Microsoft相關(guān)的漏洞的根源,并展示讓含有相關(guān)漏洞的應(yīng)用程序發(fā)生崩潰所需的具體步驟——這對于安全研究人員來說是非常有幫助的,因為應(yīng)用程序崩潰是成功利用漏洞的重要一步。
具體來說,這里使用的漏洞是CVE-2019-0618,這是一個Windows GDI+組件的遠程執(zhí)行代碼漏洞。在某些情況下,將多個普通漏洞組合在一起也足以搞定整個系統(tǒng)。鑒于此,我們認為CVE-2019-0618很可能會成為一個被廣泛利用的漏洞,因為它可用于在感興趣的目標系統(tǒng)上執(zhí)行惡意軟件。有關(guān)攻擊者使用的惡意軟件免殺技術(shù)的詳細概述,請參閱另一篇文章。
GDI+是一種基于C/C++類的API,使應(yīng)用程序能夠在視頻顯示器和打印機上使用圖形和格式化文本。實際上,基于Microsoft Win32和Win64 API的應(yīng)用程序是無法直接訪問圖形硬件的。相反,應(yīng)用程序要想與設(shè)備驅(qū)動程序進行交互,必須借助于GDI+。另外,由于GDI+可用于所有基于Windows的應(yīng)用程序,使得GDI+ API已經(jīng)成為顯示W(wǎng)indows表單并管理其內(nèi)容的主要工具。
在撰寫本文時,ZDI已經(jīng)公布了9個針對GDI+的漏洞,其中包括本文所研究漏洞。從公布的ZDI報告來看,本文研究的是 一個基于堆的緩沖區(qū)溢出漏洞,它能夠?qū)е逻h程代碼執(zhí)行。同時,根據(jù)該報告來看,該漏洞的CVSSv2得分為9.2,同時還指出,該漏洞位于DoRotatedStretchBlt方法中,這對于我們的分析工作來說是一個非常有用的線索。除此之外,它沒有公開披露與我們的調(diào)查相關(guān)的其他信息,也沒有通過針對這個漏洞的概念證明(PoC)代碼。
首先,我們想了解漏洞是如何進行修復(fù)的。基于此,我們可以推斷出漏洞的大致情況。為此,我們必須通過比較DLL打補丁后的版本與此前的版本的變化情況,從而縮小需要關(guān)注的代碼的范圍;具體的比對方法,請參考另一篇文章中的詳細介紹。簡而言之,我們需要將打補丁后的Windows 7 x86 GdiPlus DLL與未打補丁的Windows 7 x86 GdiPlus DLL進行比對。

Gdiplus.dll:未打補?。ㄗ螅﹙s已打補?。ㄓ遥?/div>
通過考察相關(guān)代碼在打補丁前后的變化情況,我們可以推斷出許多有用的信息。例如,觀察打過補丁的方法的數(shù)量,可以推測該特定更新的重要性。通過觀察和比較單個模塊的連續(xù)補丁,可以幫我們了解其開發(fā)過程,例如特定組件中出現(xiàn)的問題/漏洞的類別等。同時,這種方法還可以幫助安全研究人員識別模糊測試的目標,以及哪些功能更可能存在漏洞。
利用IDA插件BinDiff,我們可以對比二進制代碼在打補丁前后的變化情況,具體如下所示:
BinDiff的輸出結(jié)果:比較GdiPlus.dll在打補丁前后的變化情況
通過比較發(fā)現(xiàn),DoRotatedStretchBlt方法確實發(fā)生了一些變化,并且這里還具有較高的置信度。下面,讓我們詳細看看圈起來部分的變化情況。
BinDiff的輸出結(jié)果:gdiplus!DoRotatedStretchBlt方法,已打補丁與未打補丁的情況
BinDiff插件的顏色的含義:
綠色節(jié)點表示在兩個可執(zhí)行文件中具有相同指令的基本塊;
紅色節(jié)點表示比較算法無法找到等價物的基本塊;
黃色節(jié)點表示算法可以找到等價物的節(jié)點,但是在不同版本中,某些指令已經(jīng)發(fā)生了變化。
在我們的例子中,我們可以看到打過補丁的DLL中有一些紅色基本塊,所以,我們可以使用IDA Hex-Rays反匯編程序來進一步查看實際的差異。
IDA的輸出結(jié)果:GdiPlus!DoRotatedStretchBlt方法,已打補丁的版本(左)vs未打補丁的版本(右)
通過觀察這個補丁,可以看出它處理的是一種典型的安全問題——使用memcpy時沒有進行適當邊界檢查。該補丁似乎是用于處置Size參數(shù)的不同值的,以避免發(fā)生溢出。奇怪的是,并非所有的情況都得到了相應(yīng)的處理。這是因為,與Windows補丁程序的情況一樣,補丁程序不會出現(xiàn)在漏洞所在的確切位置。進一步的研究表明,該補丁程序的另一部分位于StretchBLT記錄的處理程序中:
IDA的輸出結(jié)果:GdiPlus!_bHandleStretchBlt方法,已打補丁的版本(左)vs未打補丁的版本(右)
我們注意到,除了GdiPlus!pfnIsValidEnhMetaRecordOffExt方法(該方法也存在于未打補丁的程序庫中)之外,該補丁程序中還含有另一個驗證方法,即GdiPlus!bValidateAndExpandBuffers方法。后者的作用是清理Src緩沖區(qū)(pjBits)和Ubytes(v21)參數(shù)。然后,如果滿足某些條件的話,DoStretchBlt將調(diào)用存在漏洞的函數(shù)。
IDA的輸出結(jié)果:GdiPlus!DoStretchBlt方法
如果針對hdc_flag的AND運算的結(jié)果為true,則調(diào)用相關(guān)的函數(shù)。變量hdc存放的是一個Windows設(shè)備上下文的引用。而所謂的設(shè)備上下文其實就是一種結(jié)構(gòu)體,用于定義一組圖形對象及其相關(guān)屬性,以及影響輸出的圖形模式。在我們的例子中,圖形對象包含一個用于剪切、噴涂和繪制操作的區(qū)域。
總之,要利用這個漏洞,需要控制DoRotatedStretchBlt方法的Src和Ubytes參數(shù)。所以,我們首先必須設(shè)法滿足這一要求。了解如何訪問該函數(shù)的最佳方式,就是使用IDA的調(diào)用圖功能。
針對gdiplus!DoRotatedStretchBlt函數(shù)的調(diào)用圖
如您所見,我們可以通過調(diào)用不同的處理程序(例如,bHandlePlgBlt或bHandleSetDIBitsToDev)來執(zhí)行DoRotatedStretchBlt操作。這些似乎是不同EMF記錄的處理程序。我們的問題是如何處理不同的EMF記錄?
了解EMF格式
增強型圖元文件格式(Enhanced metafile format,EMF)是一種用于存儲圖形圖像的可移植文件格式。EMF元文件中存放的是順序記錄,這些記錄經(jīng)過解析和處理后,可以在任何輸出設(shè)備上顯示文件中存儲的圖像。這個程序庫必須能夠以某種方式解釋這些記錄的類型,這樣,當處理EMF文件時,就能為其提供正確的處理程序。在二進制文件中進行快速搜索,就能找到_pdofnDrawingOrders方法:
GdiPlus!pdofnDrawingOrders存放每個記錄處理程序的引用
我們可以發(fā)現(xiàn),這里列舉了所有受支持的記錄,包括我們的bHandleStretchBlt記錄處理程序。下面,我們將設(shè)法觸發(fā)DrawingOrder操作。
IDA:GdiPlus!bParseWin32Metafile方法
GdiPlus!bParseWin32Metafile方法將解析WMF文件頭部,如果其中存在有效的EMF記錄,它將通過從_pdofnDrawingOrders調(diào)用正確的處理程序來解析它們。隨后,該解析方法會被GdipConvertEmfToWmFBits調(diào)用。除此之外,我們沒有發(fā)現(xiàn)針對該方法的其他交叉引用,這表明必定存在一些外部API。通過閱讀Win GDI+文檔,發(fā)現(xiàn)它似乎不推薦使用GdipConvertEmfToWmFBits,所以我們在Metafile類中為其提供了一個封裝器,即Metafile::EmfToWmfBits.。目前來看,我們似乎勝利在望了,至少已經(jīng)看到了曙光。
讓我們先暫停前進的步伐,回顧一下前面講過的內(nèi)容:
· 易受攻擊的方法gdiplus!DoRotatedStretchBlt是由gdiplus!DoStretchBlt調(diào)用的,而后者是用于處理EMF_STRETCHBLT記錄類型的。
· 如果為設(shè)備句柄設(shè)置了某個標志(值4),則會調(diào)用易受攻擊的方法。
· 我們最終可以通過觸發(fā)Metafile::EmfToWmfBits操作來調(diào)用易受攻擊的方法,其中一個參數(shù)是包含EMF_STRETCHBLT記錄的EMF文件。
現(xiàn)在,我們已經(jīng)通過逆向分析獲得了足夠的信息,所以逆向工作可以告一段落了;接下來,我們要做的事情是,通過模糊測試找到一個能夠?qū)е乱资芄舻姆椒òl(fā)生崩潰的EMF文件,同時也希望能找到其他讓人感興趣的東西。
模糊測試的考慮事項
1.選擇fuzzer
對于Windows二進制文件來說,當前最先進的模糊測試框架是WinAFL和Peach Fuzzing Framework。在本文中,我們將使用WinAFL,因為它是一種基于突變的、覆蓋率反饋驅(qū)動型的模糊引擎。用于Linux應(yīng)用程序的AFL的詳細介紹,請參閱這篇文章。對于插樁技術(shù),我們采用的是DynamoRio,這是一種運行時檢測框架,能夠在塊覆蓋率模式下運行。
2.創(chuàng)建測試工具
現(xiàn)在,必須創(chuàng)建一個使用易受攻擊的GdiPlus.dll庫的Windows GUI應(yīng)用程序。最終的測試工具看起來像這樣:
用于測試GdiPlus.dll庫的測試工具
首先,我們會讀取作為參數(shù)提供的EMF文件。然后,我們將啟動一個EmfToWmfBits操作(將第136行),這將有望觸發(fā)該漏洞。接著,觸發(fā)針對EMF文件的Metafile::EmfToWmfBits操作后,通過釋放使用過的組件并關(guān)閉打開的文件來清理環(huán)境。
3.樣本的生成
為了得到一個包含EMR_STRETCHBLT記錄的EMF文件,我們專門研究了WinGDI和GDI+文檔,并找到了一個簡單的生成器,具體如下所示:
用于生成包含EMR_STRETCHBLT記錄的EMF文件的生成器
我們還在初始語料庫中的EMF文件中通過了其他記錄類型以提高覆蓋率,以增加導(dǎo)致其崩潰的機會。
4.語料庫最小化與覆蓋率
對模糊測試進行優(yōu)化時,其中一個重要步驟就是語料庫的最小化。該過程將刪除無法顯著改善代碼覆蓋率的樣本,以防止在這些樣本上浪費CPU周期。利用winafl-cmin.py腳本(它是WinAFL存儲庫的一部分)處理最初的32個樣本的EMF語料庫后,最終找到了10個重要的樣本:
通過winafl-cmin.py腳本實現(xiàn)語料庫最小化
這里要做的是,在DynamoRio的運行時檢測模式下,通過測試工具運行所有初始測試用例,同時,在每次運行時捕獲感興趣的庫中已到達的基本塊。
使用IDA的Lighthouse插件,可以查看最終語料庫是否具有良好的覆蓋率:
IDA Lighthouse插件:GdiPlus.dll的代碼覆蓋率
如您所見,我們的目標GdiPlus!GdipEmfToWmfBits具有很好的覆蓋率,同時,程序庫的大部分代碼也具有很好的覆蓋率。
5.最終結(jié)果
經(jīng)過1天21小時的測試后,我們找到了80多個庫崩潰:
WinAFL的運行結(jié)果
使用BugID對它們進行分類,發(fā)現(xiàn)有多個樣本能導(dǎo)致我們感興趣的方法發(fā)生崩潰:
BugID的測試報告:GdiPlus!DoRotatedStretchBlt中可能存在RCE漏洞
使用崩潰樣本啟動WinDbg實例,結(jié)果:
WinDbg的輸出:通過模糊測試獲得的示例,觸發(fā)與CVE-2019-0618相對應(yīng)的崩潰;綠框內(nèi)是stacktrace數(shù)據(jù),紅框內(nèi)是崩潰分析數(shù)據(jù)
通過類似的方式,我們獲得了與CVE-2019-0614、CVE-2019-0618和CVE-2019-0619相關(guān)的庫崩潰。此外,我們在模糊測試過程中發(fā)現(xiàn)了一些其他方面的庫漏洞,并將這些漏洞也提交給了微軟。