MS16-030漏洞
MS16-030漏洞是Windows OLE的遠程代碼執行漏洞,由于OLE沒有正確的驗證用戶輸入,導致通過構造特殊的文件或者程序可以觸發此漏洞,導致用戶點擊后遠程執行任意代碼。
關于漏洞補丁信息:
https://support.microsoft.com/zh-cn/kb/3143136
關于漏洞說明文檔:
https://support.microsoft.com/zh-cn/kb/3143136
MS16-030補丁對比分析
最近由于對我們一些相關產品的研究,需要分析MS16-030的補丁。在分析完之后,我們發現這個補丁中關于這個漏洞的攻擊部分是很有趣的,而且把這個漏洞的一部分細節拿出來分享應該對大家都有所幫助。這篇文章主要就是重點分享從補丁對比,到觸發MS16-030漏洞的過程。
OLEAUT32.dll是Windows OLE的一個重要動態鏈接庫,我們可以看到在補丁前后對于OLEAUT32動態鏈接庫中修改過的函數的列表。
查看這個函數列表,我們選擇仔細閱讀OLEAUT32!_ResToIcon這個函數功能,不過我們也需要考慮到這個過程可能會涉及到其他函數的變動。那么首先我們這個函數可能是用來執行一些和圖標相關的一些功能,來看一下關于這個函數補丁前后的邏輯流程圖的對比。
通過對這張流程圖前后對比中可以發現,關于ResToIcon函數在邏輯執行流程上有很多變化,但是我們需要考慮每一個代碼塊執行的變化是否是由于存在安全漏洞才改變的,讓我們重點來分析每一個代碼塊。
當分析到上面的代碼塊的時候,我們發現這個很有可能是我們觸發MS16-030漏洞的關鍵部分之一,我們可以看到這里有兩個調用,一個是call SizeTAdd,還有一個是call ULongLongToULong,在文檔中我們可以查找一下這兩個函數調用:
SizeTAdd — 這個函數是OLEAUT32.dll的一組內聯函數,作用是進行算術運算并且檢驗結果的有效性,優勢是對性能影響較小。
ULongLongToULong — 這個函數也是OLEAUT32.dll的一組內聯函數,作用是進行類型轉換并且檢驗結果的有效性,優勢也是對性能影響較小。
這么看來這個位置有可能就是能觸發MS16-030的關鍵位置,那么問題來了,如何能夠讓符號路徑執行到這個分支?首先我們需要在OLEAUT32.dll中找到一個路徑的入口點,這條路徑可以執行到ResTICon這個函數。
對于符號路徑的執行我們提供了一張流程圖,正如所示,有幾條路徑都可以執行到ResTICon,通過一些試驗,我們選擇使用由vbscript.dll中調用的導入函數OLEAUT32!_OleLoadPicture(圖中黃圈所示),通過這個函數再執行到ResTICon函數位置。
那么首先我們就需要分析一下vbscript.dll中的導入函數的執行路徑,通過IDA pro打開vbscript,通過流程圖我們可以看到導入函數OLEAUT32!_OleLoadPicture的執行過程,流程非常簡單。
可以看到,在vbscript.dll中經過幾次調用,會執行到_imp_OleLoadPicture位置,這個imp實際就是在vbscript.dll中執行到該位置時會調用OLEAUT32.dll中的OleLoadPicture函數,這樣就和之前我們分析的幾種分支中我們選用的部分重合了。
這樣看來,我們只要調用vbscript!VbsLoadPicture就有可能執行到我們需要的分支流程中,可以通過vbscript LoadPicture的API調用到這個vbscript函數,從而執行到OleLoadPicture函數分支。
PoC構造
我們嘗試使用這種方法,首先在vbscript.dll中調用導入函數OleLoadPicture的位置下了斷點,然后創建了一個圖標,這個圖標加載時會調用LoadPicture的這個API,也就是會調用VbsLoadPicture函數,打開這個圖標,但是卻沒有命中斷點。我們只能來檢查一下這個過程為什么沒有命中斷點。
通過分析,我們發現在調用VbsLoadPicture之后,執行過程中會先調用一個call __imp__StgIsStorageFile,這個函數也是OLEAUT32.dll中的函數,正是ole32!StgIsStorageFile導致的無法執行到OleLoadPicture,那么這個函數的是什么呢?
通過查看MSDN,我們只找到關于StgIsStorageFile函數很少的信息,這個函數主要用來表明特定磁盤中是否包含存儲對象。
通過Google檢索我們需要從VbsLoadPicture到OleLoadPicture的這個執行流程,其實這個過程是一個類似于COM結構化存儲的執行過程,通過MSDN可以找到一些關于這個過程的信息。
“結構化存儲就是將單個文件以數據和流的形式作為一種結構化集合來處理,從而在COM中保持數據和文件的持久性”。
這樣看來,我們需要創建一個結構化的存儲對象然后插入一個圖片,來使函數執行到OleLoadPicture函數調用位置。那么這樣的一個文件我們應該如何創建呢?
進一步通過Google搜索,我們找到了一款叫做OpenMCDF的工具,下載地址是: http://openmcdf.sourceforge.net。這樣我們要做的就是在存儲化結構的根節點位置創建一個包含圖片的流(譯者話:這個存儲化結構的概念在很多地方都有用到,比如Office文檔,pdf文件等等),然后通過vbscript再次打開這個文件調用LoadPicture函數。我們使用OpenMCDF工具中一個結構化存儲的界面。
通過這個界面創建文件之后,我們再次在vbscript.dll中的OleLoadPicture下斷點,但是還是沒有命中,這是什么原因呢?
經過分析,我們發現,在調用OleLoadPicture之前,還會經過另一處檢查。
實際上我們執行的過程執行了粉色塊的部分,而OleLoadPicture調用則在右邊,可以看到在之前會調用ole32!CExposeDocFile::OpenStream,這個過程會有一個對流名稱“CONTENTS”的檢查,如果正確則會進入右邊分支(我們想要的)處理,因此,我們需要在OpenMCDF中修改流的名稱,改為CONTENTS。
這樣修改之后,我們的文件打開之后,就能成功命中OleLoadPicture斷點了。接下來我們通過Windbg附加這個任務來跟蹤,同時在OLEAUT32.dll中的ResTIcon函數下斷點,來確定OleLoadPicture之后是否能夠命中我們分析到的漏洞觸發的關鍵函數。
可以看到,我們確實命中了斷點,到這里為止,我們就很接近之前分析到的發生改變的代碼分支了,但是,實際上我們利用OpenMCDF生成的文件是一個正常的文件,也就是說其中包含的圖標的這個流也是正常的,那么接下來,我們使用dumb fuzzer(譯者注:dumb fuzz是一種fuzz技術,不管文件格式問題,完全采用生成全隨機化的數據來進行fuzz)的方法來生成畸形文件,通過一個vbscript的腳本來執行文件,再通過eEye公司的一款產品來自動化調試。當然,我們也要通過Windbg中個gflags開啟頁堆監視(譯者注:通過Windbg下的gflags.exe /I [process] +ust +hpa開啟頁堆監視)。
我們寫了一個簡單的dumb fuzzer的腳本。
接下來運行這個fuzz腳本,并且通過eEye公司的工具跟蹤,大概在619次測試過后會eEye的工具會捕獲到一次崩潰,利用Windbg加載這個崩潰文件。
到此,我們通過補丁復現了這個Windows OLE的漏洞,并且在打上補丁之后我們發現再用這個PoC無法觸發漏洞了。
分析補丁、補丁對比是一個非常有趣的過程,但實際上使用bindiff(譯者注:一款非常好用的補丁對比工具)這樣的補丁對比工具可以大大加快我們的分析進度,而且有助于我們全面了解補丁前后動態鏈接庫的變化情況,以便分析出觸發漏洞的執行路徑。同樣,將文件類型轉換成結構化存儲去測試也有可能成為未來漏洞挖掘一個很有意思的方向。