看了趨勢的博文後嘗試手動構造了一下CVE-2018-8373的expoit,大概花了我一個下午的時間,晚上回來記錄了一下整個過程。
本文僅作分享,不足之處請見諒。
內容見下文。(原文為:內容見附件)
看了趨勢的博文後嘗試手動構造了一下CVE-2018-8373的expoit,大概花了我一個下午的時間,晚上回來記錄了一下整個過程。
本文僅作分享,不足之處請見諒。
內容見下文。(原文為:內容見附件)
CVE-2018-8373 是趨勢科技上個月抓到的一個 vbscript UAF 0day,它是前幾個月 CVE- 2018-8174 的兄弟漏洞。由於趨勢的博文已經把漏洞原理和整個利用過程講得十分清楚, 所以看完文章後的我只有一個問題:如何寫一個 exp?
趨勢公佈的 hash 在 VT 上是沒有的,而且文章中只給了 PoC 和部分高度混淆的在野利用片段,出於這個原因,我想自己構造一個 exp,供自己和組內的小夥伴分析除錯使用。
由於這個漏洞的觸發原理有一點點 0189 的影子,又有一點 8174 的影子,利用部分還有一點點 6332 的影子。所以在熟悉前 3 個漏洞的前提下,寫出這個漏洞的 exp 應該不難,於是我白天花了幾個小時試著寫了個 exp。
鑑於此漏洞潛在的危害性,完整程式碼不予公開,本文僅記錄構造過程中的一些細節。
UAF 的原理趨勢已經講的很清楚了,由於從頁堆資訊來看,每個二維陣列的 tagSAFEARRAY 結構佔記憶體空間為 0x30(實際的 tagSAFEARRAY 前面還多申請了 0x10 位元組)。可以仔細看趨勢的截圖,或者自己動手做一下。
所以為了順利 UAF,我們需要設計 pvData 部分大小為 0x30 的一維陣列,也就是有 3 個元素的一維陣列,令其為 array(2)。
我們對照趨勢的圖 4/ 圖 11 , Get P 回撥中對 array 陣列的大小進行了更改: array(2)->array(100000),隨後利用 array(i) = array2 迴圈申請大量小空間的二維陣列 array2 , array2 被定義為 array2(0, 6)。此過程需要申請大量二維陣列的tagSAFEARRAY 結構體,其中有一個會佔用原來 array 的 pvAata 記憶體,並在 Get P 返回之後將 tagSAFEARRAY. rgsabound[1].cElement 覆蓋為 0x0fffffff。
所以下一步就是找出哪個array2 的頭部佔用了原來 array 的 pvAata 記憶體,這個過程可以參考趨勢圖 16。
思路其實很簡單,我們遍歷新的 array 陣列,找出其中的一維大小為 0x0ffffffe 的array2。我們假設這個 array2 在 array 中對應的下標為 index_vul。如下:
找到 array2 之後,我們可以用 array(index_vul)(a, b)來索引這個陣列,其中 a∈[0,0xffffffe],b∈[0,1]。這個陣列可以越界讀寫 array(index_vul)(a, b).pvData
開始的一大片記憶體。
現在我們擁有一個可以越界讀寫的陣列,最終目的是要去實現任意地址讀寫。下一步如何進行?
從趨勢的圖 15/圖 16 可以看到,這個漏洞藉助了和 6332 一樣的思路來進行利用,即藉助大量連續記憶體申請來實現多個 array2.pvData 記憶體的連續,每個 array2.pvData 中間間隔 8 位元組的堆資料。我們利用越界讀寫能力通過對 8 位元組的錯位進行賦值來改變 variant 變數在記憶體中的含義,從而構造一個基地址為 0,元素大小為 1,元素個數為 0x7ffffff 的一維陣列;同時再洩露一塊記憶體的地址供讀寫使用。從而實現任意地址讀寫。
現在的問題是:如何找出趨勢圖 14/15 中的 index_a 和 index_b?關於這點,原文中說得含糊其辭,而且從混淆的片段(圖 14)看去步驟較為複雜,還涉及到部分沒有出現在程式碼片段中的函式。
我在放棄理解圖 14 的程式碼片段後,轉而開始思考有沒有簡單一點的辦法。從趨勢圖 15 我們可以看到index_a 和 index_b 所需要滿足的位置。偵錯程式中,我們可以看到 index_a 具有如下特徵。
可以看到array(index_vul)(index_a-8, 0)至array(index_vul)(index_a-1, 0)都儲存著短整型的3對應的variant結構,到了array(index_vul)(index_a, 0),由於堆結構資料,錯位了8個位元組,導致array(index_vul)(index_a, 0)代表的variant和前面後不一樣。到了array(index_vul)(index_a+1, 0)。由於我們之前給每個array2元素都設定為3,見趨勢圖10。所以array(index_vul)(index_a+1, 0)開始代表的都是儲存著長整型2對應的variant結構。
VB 有一個函式 VarType,可以用來判斷 variant 的型別:
我們可以藉助其實現如下邏輯:
這樣我們就找到了滿足條件的 index_a。
現在我們已經找到了 index_a,如何找到它對應的 index_b 呢?這個則更簡單,我們可以利用越界寫將array(index_vul)(index_a, 0)設定為一段字串,例如“AAAA”,這樣某一個 array2 的 Data 域就變成了 8( 低 2 位元組)。我們遍歷 array 陣列, 找出array(index_b)(0, 0)=8 的 index_b 即可,如下:
當然,上述做法存在一定的失敗率,我相信原 exp 中的校驗一定會更保險。但是這樣的 exp對於本地除錯來說,足夠了。
在取得 index_a 和 index_b 之後。我們就得到了兩個可以用來進行型別混淆的 variant 結構。後續就是藉助假的字串資料和型別混淆構造一個超長一維陣列了。
我們模仿yuange的6332利用程式碼,令array(index_vul)(index_a, 0)處儲存我們構造的字串地址,令array(index_vul)(index_a+2, 0)處儲存我們構造的用來偽造陣列的字串地址。然後藉助array(index_b)(0, 0)和array(index_b)(2, 0)去分別改動兩處的型別,令array(index_vul)(index_a, 0)處的variant 轉化為長整型,令array(index_vul)(index_a, 0)處的variant轉化為陣列,整個過程只需要如下幾句程式碼。從而獲得一個超長陣列和一個被洩露的字串地址。
我們在偵錯程式中看一下上述過程前後 array(index_vul)(index_a+2, 0)的資料變化。
rw_primit 第一句程式碼執行後:
執行rw_primit後:
現在,我們有了一個塊洩露的記憶體 util_mem,一個可以實現 32 位下使用者態任意地址讀寫的一維陣列array(index_vul)(index_a+2, 0),接下來就是利用這兩個工具封裝任意地址讀寫函式了。
任意地址讀:
任意地址寫是一樣的原理,此處不再貼出。相關原理在其他分析 6332/0189/8174 的文章中已經有很多詳細說明,此處不再提及。
藉助上述封裝好的函式,將 8174 的利用程式碼稍微改造一下就可以實現後續步驟:
當然 8174 這種利用方法並不能躲避 ROP 檢測,還是當年的 GodMode 比較經典。
anyway,經過幾個小時的努力,我成功地在打了 2018 年 7 月份全補丁的 win7 x86 sp1IE11上彈了一個計算器:
像 CVE-2018-8174 和 CVE-2018-8373 這兩個漏洞,思路上基本是 yuange 的漏洞利用技術+TK 的利用技術,威力可見一斑。
參考連結:
https://blog.trendmicro.com/trendlabs-security-intelligence/use-after-free-uaf-vulnerability-cve-2018-8373-in-vbscript-engine-affects-
本文原作者:銀雁冰(看雪ID)於 2018/08/17釋出在看雪論壇
原文連結:https://bbs.pediy.com/thread-246327.htm
轉載請註明轉自看雪論壇。