1. 程式人生 > 其它 >一個二進位制POC的誕生之旅CVE-2018-0802

一個二進位制POC的誕生之旅CVE-2018-0802

背景

在潛伏17年的“噩夢公式”漏洞(CVE-2017-11882)被曝光修補之後,之前的漏洞程式EQNEDT32.EXE在windows 10系統下仍然沒有開啟ASLR保護,因此其利用非常容易,在修補之後可以發現微軟的釋出的是二進位制補丁而不是對該程式原始碼進行重新編譯,因此猜測該程式的原始碼可能已經遺失,如果真實情況是這樣,那麼公式編輯器這個程序將成為其他漏洞發現和利用的“聖地”,因為微軟將很難從原始碼級別去排查這個程式是否還有其他漏洞。此次“噩夢公式”的姊妹篇CVE-2018-0802就在這一背景下被業界所發現。

概述

雖然微軟在修補CVE-2017-11882漏洞使已經強制對公式編輯器程序開啟了隨機基址(ASLR),這使得漏洞的利用門檻提高,但由於補丁程式中的偶然因素使得能通過一個比較簡單的方式繞過ASLR機制從而利用該漏洞。

由於此次漏洞也是由於“Equation Native”流中出現的問題,因此我們先了解一下它的大致結構,整個”EquationNative”資料由頭結構和後續資料組成。其頭結構為:

struct EQNOLEFILEHDR {    WORD    cbHdr;        // EQNOLEFILEHDR長度,恆為0x1c    
DWORD   version;    // 恆為0x20000    
WORD    cf;              // 剪下板格式("MathType EF")    
DWORD   cbObject; // MTEF資料長度,不包括EQNOLEFILEHDR部分    
DWROD   reserved1;// 未公開    
DWORD   reserved2;// 未公開    
DWORD   reserved3;// 未公開    
DWORD   reserved4;// 未公開};

而緊接著頭結構內容的是MTEFData內容,MTEFData內容也由MTEF頭和MTEF位元組流資料組成,MTEF頭內容:

struct MTEF_HEADER {    BYTE bMtefVersion;            // MTEF版本號,一般為0x03    
BYTE bPlatform;                  // 系統生成平臺,0x00為Mac生成,0x01為Windows生成    
BYTE bProduct;                   // 軟體生成平臺,0x00為MathType生成,0x01為公式編輯器生成    
BYTE bProductVersion;       // 產品主版本號    
BYTE bProductSebVersion; // 產品副版本號};

MTEF位元組流資料包括一系列的記錄,每一個記錄以一個標籤位開始,標籤位的低4位描述該記錄的型別,高四位描述該記錄的屬性,後續緊跟標籤的內容資料,由於此次發生棧溢位的部分在於字型標籤,因此對字型標籤進行了解:

struct stuFontRecord {    BYTE    bTag;               // 字型檔案的tag位0x08    
BYTE    bTypeFace;     // 字型風格    BYTE    bStyle;             // 字型樣式    
BYTE    bFontName[n] // 字型名稱,以NULL為結束符};

經過以上的介紹我們對於公式資料流這部分內容有了一定的瞭解。由於已經知道了這次是由於拷貝字型檔案內容時產生的棧溢位,那麼我們可以以CVE-2017-11882漏洞的POC為基礎進行手工測試,根據Font的結構我們可以發現可以在bFontName欄位做一些文章,因為是以NULL為結束符的,因此我們可以通過構造長度超越平時長度的字元來試一試是否會產生異常事件。

圖1 CVE-2017-11882構造的漏洞觸發現場

圖2 構造的畸形資料

圖3 捕捉到異常事件

開啟構造的畸形檔案後捕捉到異常事件,發現該異常事件發生在EQNEDT32.EXE檔案中,但異常事件並沒有像普通的棧溢位漏洞那樣給我們洩露一些關鍵性資訊,至今我們也只能知曉這構造的異常資料確實引發了EQNEDT32.EXE檔案的c0000005異常,那麼我們可以使用偵錯程式進行除錯看看到底內部是什麼情況。

由已經獲知的資訊我們基本可以定位到可疑的大致程式碼位置,那麼我們啟動EQNEDT32.EXE程式並用OD附加該程序,然後去可疑的函式位置下個斷點然後F9跑起來,之後開啟我們構造的檔案發現OD斷在了該可疑函式中,我們進一步去單步走走,發現第三個call的引數很可疑,該函式第一個引數指向的記憶體內容和我們構造的畸形資料相同,且第三個引數也指向了附近的棧地址,因此可以使用IDA檢視該函式內部情況。

圖4 可疑呼叫函式

圖5 棧溢位的函式

這是不經長度校驗的拷貝,而傳入的lpLogfont就是我們構造的資料,證明在此處我們可以製造一個棧溢位,觀察此時的拷貝起始地址位於上一個呼叫函式的棧幀中,因此利用本函式實現覆蓋返回地址執行程式碼的做法是不行了,那麼我們現在可以控制覆蓋上一個棧幀的函式返回地址,觀察棧的結構可以發現拷貝起始地址據上一個函式棧幀返回地址相差0x94(0x44EC24-0x44EB74-0x1C),在覆蓋了0x94位元組後我們便可以覆蓋上一個函式的返回地址。

圖6 函式呼叫棧

但是要實現這一點得保證我們對於棧中資料的修改不會引起上一個呼叫函式中發生其他函式呼叫時出現異常,當然我們可以抱著僥倖心態去試試到底能不能行得通,畢竟我們更想看到這個棧溢位能不能被用起來,那麼在回到上一層函式後我們大膽的使用F8單步步過,每當能正常的通過一個函式時我們便朝著終點更進一步,如果能夠順利的走完整個函式那麼證明我們就能劫持到執行流,使用IDA檢視我們要到達終點之前需要正常越過的函式,然後就抱著賭徒心態來使用F8越過這些可能會導致異常的函式。

圖7 呼叫函式概覽

當然事實證明任何抱有僥倖心理的行為都是作死,在使用F8進行賭一把的情形下,越過圖8所示位置的函式時發生崩潰退出,重新附加公式編輯器再來到崩潰現場發現此處原來就是圖7處呼叫CVE-2017-11882修補函式前的”_strcmpi(lpLogfont, &Name)”,原因是lpLogfont指標被覆蓋了,那麼我們重新調整我們構造的畸形檔案,之前構造時資料太大導致覆蓋了返回地址後接著又覆蓋了上一個棧幀的資料,因此我們精確構造0x94大小的資料使返回地址恰好被覆蓋且不破壞上一層棧中資料,之後重新附加除錯重複我們的”賭博”。

圖8 越過該函式時發生崩潰

圖9 重新調整構造資料後成功越過該函式

調整我們構造的資料大小之後重新附加除錯,再次到這個位置時我們成功的越過了這個函式,證明我們離終點更進一步,然後在一路F8越過,突然我們命中了一個斷點,檢視斷點的位置就是這個函式自身,在這裡函式進行了遞迴呼叫,這對於我們來說並不算一個好事,這導致了我們可能離最終的ret又增加了許多障礙,但我們在此時別無他法,只能強行F8再一路往終點走。

圖10 命中斷點的函式位置

慶幸的是這個遞迴呼叫用一個遠跳很快的就跳出了函式主體,甚至於連棧溢位的函式都沒有機會執行,那麼證明我們運氣還是非常好的,在這個函式ret之後需要我們越過的函式呼叫就沒有多少了,希望就在眼前。

圖11 遞迴呼叫成功F8步過

退出遞迴呼叫函式後,繼續靠著我們一手絕活F8最終我們來到了我們想要的終點,發現返回地址被我們構造的資料成功覆蓋,證明該漏洞可以被利用。

圖12 最終的ret成功劫持執行流

那麼現在我們知道了我們可以構造一個長度為0x94位元組長度的內容來實現一個shellcode,然後覆蓋返回地址就能成功劫持執行流,通過除錯發現在覆蓋的返回地址之下,也就是函式呼叫的第一個引數剛好指向我們構造的源資料,那麼我們在返回地址只需要一個ret指令,便能成功引導執行流從我們的shellcode開始執行,但是在CVE-2017-11882漏洞被曝光之後微軟對公式編輯器程序強制開啟了動態隨機基址,意味著我們不能寫死返回地址,因為該地址每次都在變化,那麼總有大神有奇招(http://www.freebuf.com/column/160006.html),參考該篇文章介紹的繞過ALSR的手法我們也可以成功的bypass ALSR。原理大致就是隨機基址通常只隨機高兩位地址,而在記憶體中由於小端排序的原因,我們覆蓋是從低兩位開始的,那麼由於末尾必須用NULL截斷,因此倒數第二位地址必須為00,查詢是否具有0xXXYY00ZZ(X,Y,Z為隨機字元)格式的ret指令,最終發現事實就是這麼偶然,剛好該函式內部有這麼一個地址。

圖13 用於bypass ALSR的retn指令

我們的shellcode結構已經比較清晰了,前面0x94位元組用於寫入我們的shellcode,緊接著我們用“x25x00”覆蓋返回地址低兩位,那麼就開始佈局我們的shellcode,由於shellcode空間只有0x94位元組的空間十分有限,因此我們可以考慮模仿CVE-2017-11882中使用的手法利用程序中的WinExec函式來完成呼叫其他程式的方法。

圖14 WinExec函式仍然存在

最終寫出彙編程式碼提取出shellcode放入檔案中用於構造字型檔名稱的位置,也就是之前我們構造的”aaa…”處,並在覆蓋起始地址偏移0x94位元組後填入關鍵的覆蓋返回地址的資訊”x25x00”,那麼在檔案中的shellcode就這麼構造成功了,我們把需要執行的命令列引數緊接著寫到shellcode之後便可以實現一個利用CVE-2018-0802漏洞執行的rtf檔案,當然這需要主機打上CVE-2017-11882的補丁之後才能成功復現。

圖15 構造的shellcode

用這個shellcode從我們開始構造”aaa…”的起始地址開始覆蓋,在之後緊接我們需要執行命令(先用開啟計算器試個水),然後用不為NULL的字元進行填充至一共0x94位元組,再用我們的bypass ALSR的”x25x00”來覆蓋返回地址低兩位,那麼一個簡單的漏洞利用文件就產生了。

圖16 利用CVE-2017-11882改造成的CVE-2018-0802漏洞利用文件

圖17 開啟文件後自動彈出計算器

至此利用CVE-2017-11882漏洞檔案進行一個簡單的改造,我們完成了CVE-2018-0802檔案的構造,開啟該文件後自動彈出計算器,但是這並不是我們最終的利用形態,接下來我將展示攻擊者如何利用該漏洞進行惡意行為,這裡其實又利用了rtf文件的一個特性,在開啟rtf文件時會將嵌入文件中的檔案自動釋放到系統%temp%目錄下,因此現今最多嘗試攻擊的方式都是利用這個特性在rtf文件中嵌入惡意EXE檔案,然後再利用該漏洞去執行釋放在%temp%目錄下的檔案。首先修改我們構造的用於啟動計算器的執行命令,更改為”cmd /c %temp%hello.exe”之類的命令。

圖18 修改為執行其他EXE程式的命令形態

接下來我們新建一個任意型別的檔案,改字尾名為.rtf,用word開啟後拖入我們想要執行的exe檔案,這個exe檔案應該要和修改的命令中檔名對應,然後儲存用二進位制編輯工具開啟,將剛才我們構造那部分惡意執行程式碼的整個object內容拖放到存放了exe檔案的rtf中指定的位置。

圖19 嵌入exe檔案並儲存

圖20 需要拷貝到嵌入exe的rtf文件中的惡意公式編輯器內容

圖21 拷貝到嵌入exe檔案的rtf文件中的大致位置

至此真正一個具有攻擊性的rtf文件就形成了,該文件在被word程式開啟後將會由於自身機制釋放hello.exe檔案到%temp%目錄下,之後由於公式編輯器的漏洞利用了WinExec函式執行了”cmd /c %temp%hello.exe”,實現了最終的攻擊行為,當然我在這裡用的示例程式只是一個提示中毒的標籤,並沒有真正的攻擊行為。

圖22 開啟文件後執行我們嵌入rtf文件中的任意exe檔案

之前利用CVE-2017-11882的漏洞也可以這樣非常簡單的構造一個惡意文件出來,由於這個漏洞的利用手法非常簡單,只需要一個漏洞檔案和一個二進位制編輯器即可實現利用並發動攻擊,因此其破壞性不容忽視,建議廣大使用者趕緊打上最新的補丁,以防止此類攻擊,最新的補丁徹底拋棄EQNEDT32.EXE檔案,從而杜絕了攻擊者想利用該漏洞”聖地“程序進行漏洞利用的行為。

參考文章

http://www.freebuf.com/column/160006.html

https://research.checkpoint.com/another-office-equation-rce-vulnerability/

POC地址

https://github.com/GeekOnlineCode/POC