當前位置:
首頁 > 新聞 > 一個二進位POC的誕生之旅CVE-2018-0802

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

*本文作者:GeekOnline,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。


背景


在潛伏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


*本文作者:GeekOnline,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 FreeBuf 的精彩文章:

任意用戶密碼重置(一):重置憑證泄漏

TAG:FreeBuf |