FavoriteLoading
0

網頁掛馬常見漏洞分析與檢測

#

事件描述

一、cve-2018-8373漏洞的初步分析

2018年8月15日,趨勢科技披露了他們發現的一起瀏覽器漏洞攻擊事件。在檢測到的攻擊流量中,攻擊者使用了cve-2018-8373這個漏洞來攻擊IE瀏覽器,訪問存在該漏洞的頁面可能導致遠程代碼執行。有經驗的研究員應該了解,在大部分的EK工具包中,針對IE瀏覽器的漏洞經常被用來做網頁掛馬,國外通常稱為“Drive-By-Download”攻擊。加上該漏洞使用的混淆方式和之前披露的cve-2018-8174類似,漏洞利用代碼估計是同一批人所編寫的。而趨勢科技在報告中提到他們通過啟發式分析檢測到了該流量,目前來說主流的瀏覽器漏洞檢測方式分為靜態和動態兩類,靜態檢測是匹配其觸發漏洞以及漏洞利用的關鍵代碼特征,動態檢測則是檢測瀏覽器的敏感行為,當然也有利用機器學習對惡意網頁進行分類、檢測的技術,不再詳述。接下來先簡單分析下cve-2018-8373。

由于沒有拿到攻擊樣本,只能依據簡單的poc來進行分析、復現。poc的代碼如下:

圖片1.png

接下來我們有兩種調試的方式,第一種是利用windbg加載IE瀏覽器,并通過開啟頁堆來跟蹤漏洞的觸發點。第二種是將其中的vbscript代碼提取出來,寫入到vbs腳本文件中,利用wscript.exe程序加載vbs腳本,再進行分析。由于該demo并沒有使用到document相關的api,加上使用wscript.exe調試起來相對簡單點,所以這里使用第二種方法進行調試,接下來通過調試來看下poc中對應的二進制程序的代碼實現。在win環境下,由vbscript.dll對vbs腳本進行解釋執行,通過ida打開vbscript.dll并且加載符號,可以看到一些意義性較強的函數,大致能根據這些函數名稱來定位具體的vbs腳本中的代碼。

在poc中,將MyClass通過new進行創建并賦值給指定變量cls,該操作首先會觸發類的創建以及初始化,創建類的函數由vbscript!VBScriptClass::Create函數完成,在初始化類的各項屬性后,將該class封裝為一個variant變量,class的創建如下:

圖片2.png

最后會將該class封裝成一個variant類型的變量

圖片3.png

在微軟的官網以及windows kits的頭文件中均可以查到variant types的定義,部分如下:

圖片4.png

針對不同類型的variant變量解釋如下:

圖片5.png

創建類成功后則會調用vbscript!VBScriptClass::InitializeClass函數對class的內容進行初始化?:

圖片6.png

在vbscript!VAR::IsFunction函數中獲取class指針

圖片7.png隨后調用class的虛函數vbscript!CScriptEntryPoint::Call?進行初始化,最終的調用棧如下:

圖片8.png調試過該vbs漏洞的研究人員應該了解,vbscript!CScriptRunTime::RunNoEH負責對編譯后的vbs代碼進行解釋執行。這里執行類的初始化操作,主要包含了array數組的定義以及Class_Initialize函數的執行。vbscript中創建數組的函數為vbscript!MakeArray,如下:

圖片9.png其中參數a1代表傳入的數組的維度個數,由于poc中定義的數組維度為空,因此a1為0,因此直接返回0,array數組并沒有構造成功。

圖片10.png

隨后在函數Initialize_Class中,利用Redim函數將array數組重新定義為一個數組長度為3的一維數組。但由于上次創建數組失敗,其實這里并沒有如同趨勢科技中的報告所說,調用vbscript!RedimPreserveArray函數來對數組進行重新分配,而是再次調用了vbscript!MakeArray這個函數創建了一個數組。在調試過程中分別對vbscript!MakeArray以及vbscript!VBScriptClass::InitializeClass下斷點,會發現先命中MakeArray后命中InitializeClass,緊接著再次命中MakeArray。

圖片11.pngvbscript中的數組類型為tagSafeArray,結構如下:

圖片12.png

內存中的結構如下:

圖片13.png

看接下來的poc:

圖片14.png

該段代碼執行時會先后調用vbscript!AccessArray、vbscript!AssignVar函數來獲取cls.array(2)的具體地址。在AccessArray函數會對cls數組進行檢測、訪問并獲取cls第二個數組的地址,隨后賦值給第二個參數,并在vbscript!AssignVar函數作為參數傳入,在AssignVar函數中會將該地址賦值給esi,并在函數退出前將數據復制到該地址的16個字節。漏洞的觸發點就在這里,函數在開始就確認了需要進行賦值操作的數據地址,但是在獲取源操作數(這里指cls類)時,發生了對被賦值地址的釋放操作,并且也沒有對該地址進行檢查,最終導致了釋放后重用。在獲取源操作數時,會將cls類的variant變量傳入并進行檢測,當檢測到源操作數類型為0x9時,會調用vbscript!VAR::InvokeByDispID函數來獲取cls的對應值。在一般情況下,是無法將一個類的地址信息傳遞給一個數組的,vbscript并不支持這么做,會報如下錯誤:

圖片15.png

但是由于MyClass定義了Public Default Property Get P,因此實際上會將P的值作為cls的返回值返回給目標,類似的代碼在cve-2018-8174中同樣被使用,因此在考慮檢測漏洞觸發代碼時可以將它作為關鍵的檢測因素。

圖片16.png

在vbscript!assignvar函數中對cls訪問時會調用到如下函數,并將P的值返回給棧上的第4個參數。

圖片17.png接下來會調用下面的代碼:

圖片18.png

該段代碼會觸發vbscript!RedimPreserveArray函數來對array數組進行重新賦值,代碼如下:

圖片19.png主要的實現在oleaut32!SafeArrayRedim函數中。當申請的數組的長度超過原有的數組長度,會申請新的內存來覆蓋原有的safearray中的pvdata指針,并修改safearray的rgsabound區。整個調用堆棧如下:

圖片20.png隨后通過vbscript!AssignVar函數獲取P的數據,并返回給指定參數,可以看出其調用堆棧是一致的。

圖片21.png當從InvokeByDispID返回到AssignVar時,會將P的值拷貝到原來的cls.array(2)地址處。

圖片22.png

圖片23.png

但此時該處的地址已經被釋放,

圖片24.png這就造成了一個uaf漏洞。該漏洞的利用方式是可以向被釋放的內存寫入指定數據,即對應數組pvdata+數組偏移+0x8的位置,剛好該釋放的堆塊的大小為0x30,而一個二維的tagSafeArray格式的數組的大小同樣為0x30,趨勢科技的分析報告中的樣本通過構造大量的二維數組以占用被釋放的內存,而本身二維數組的長度是由兩個維度長度的乘積決定的,而該通過該漏洞則可以偽造二維數組中第一維度的長度為任意值。這樣有了一個偽造的超長二維數組,由于堆塊頭部的存在,導致后續申請的二維數組存在8字節的錯位,導致可以修改偽造的二維數組的頭4個字節,這樣相當于可以控制一個variant變量的類型以及具體數據,隨后將偽造的長度為0x7ffffff的一維tagSafeArray數組通過字符串的形式寫入,并更改其變量類型為VT_ARRAY|VT_VARIANT,可以偽造一個首地址為0,長度為0x7ffffff的一維數組,通過該數組實現全局讀寫,詳細的利用方式可以參考看雪論壇上的兩篇文章。

二、漏洞觸發流程的思考以及漏洞檢測

思考本次漏洞觸發的核心流程,關鍵就在于vbscript中array數組的創建、賦值、重構,本次的漏洞觸發中所利用到的關鍵函數如下圖:

圖片25.png

這里不禁一個疑問,為何需要調用兩次makearray來創建數組,直接在定義數組時創建一個定長數組不就省下了一部分漏洞觸發的代碼嗎?帶著這樣的疑問,重新修改了poc,并刪除了Initialize_Class函數的內容,如下:

圖片26.png

結果返回一個運行時錯誤:

圖片27.png

為了了解該錯誤的原因,對array數組的創建進行了跟蹤調試,在poc中,數組的名稱為array,在調試過程中發現通過vbscript!VbscriptClass::CreateVar函數創建了名為array的變量:

圖片28.png隨后調用vbscript!MakeArray創建了array的tagsafearray結構以及對應的數組空間,并將結構的地址寫入到array變量+0xc的偏移處,代碼如下:

圖片29.png

最終的結果如下圖:

圖片30.png在調用vbscript!RedimPreserveArray過程中,會對傳入的數組類型進行檢測,具體代碼如下:

圖片31.png

當檢測到array數組的feature屬性&0x10的結果為1時,反回0x8002000D錯誤。可以看到通過Dim array(2)定義的tagsafearray結構的feature屬性值為0x892,查看微軟對feature的解釋如下:

圖片32.png

0x892的值為0x880|0x12得來,具體代碼如下:

圖片33.png

當數組成功創建后,會將其feature屬性值同0x12進行邏輯或,結果就會創建一個靜態數組,而靜態數組是不可以被重新修改其數組大小的,該類型的數組也就不存在釋放后重用漏洞。而通過Redim array()創建的是一個動態的tagSafeArray結構體數組,初始化的結果就是array變量+0xc處的數據為0,第一次調用Redim函數的時候,會先從vbscript!vbscriptclass::createvar創建的array變量+8的地方獲取到array數組地址,經過判斷后array數組的地址為0,最終調用了vbscript!MakeArray函數來創建一個新的數組,具體的代碼邏輯如下:

圖片34.png

這里同樣思考另外一個問題,在漏洞觸發過程的第二步僅僅是對array數組的重新創建,是否必須要利用到Initialize_Class該函數來執行,答案當然是非必需的,這里僅需要讓array在被vbscript!RedimPreserveArray函數調用前具有長度為3的數組即可,因此可以構造如下的利用demo,也可以實現相同的效果。

圖片35.png

這樣在考慮對該漏洞進行靜態檢測時,在不考慮樣本被混淆、加密的情況下,首先應該是對Public Default Property Get以及Redim、Redim Preserve等關鍵字進行檢測,至于Initialize_Class關鍵字則不是必要檢測字段,如果做動態檢測,則可以對如vbscript!MakeArray、vbscript!RedimPreserveArray、vbscript!AssignVar等函數hook,對數組的創建、重新分配、賦值等操作做檢測。當然這里僅僅是對漏洞觸發的部分代碼進行檢測,其實針對漏洞利用的代碼如heap spary、rop、RW對象等進行檢測會更加有效。

原文鏈接:http://hack-sec.top/?post=22

【聲明】:8090安全小組門戶(http://www.jvwkvg.tw)登載此文出于傳遞更多信息之目的,并不代表本站贊同其觀點和對其真實性負責,僅適于網絡安全技術愛好者學習研究使用,學習中請遵循國家相關法律法規。如有問題請聯系我們,聯系郵箱[email protected],我們會在最短的時間內進行處理。