VBScript in 2018
0x00 背景
VBScript是微軟開發的一款指令碼語言,是Visual Basic的輕量級的版本。使用VBScript可以快速高效地開發出一些瀏覽器客戶端和服務端的應用程式。然而隨著DVE技術的公佈以及在CVE-2014-6332、CVE-2016-0189中的穩定利用,VBScript開始被認為並不是那麼安全了。很快微軟在Windows 10 Fall Creators Update版本中對於Internet Explorer(IE)瀏覽器的Internet Zone和Restricted Sites Zone預設禁用了VBScript。但是這似乎並未減緩關於VBScript的在野漏洞利用攻擊,相反,隨著Adobe宣佈2020年Flash正式退役,Hacker們似乎開始把重點轉向了VBScript這個幾乎被遺棄的指令碼引擎。於是在2018年我們看到了CVE-2018-8174,CVE-2018-8373 這類0Day攻擊事件以及相當數量的關於VBScript的CVE和研究報告。
筆者在學習了部分研究報告和除錯了部分PoC後,決定對2018年VBScript的漏洞做個簡單的歸類總結,與大家分享,希望對關於VBScript的漏洞挖掘和分析有所幫助。但水平有限,文中錯誤之處懇請斧正。
0x01 分析
2018年關於VBScript的CVE不少,這裡筆者篩選了6個比較有代表性的CVE分兩類進行分析。
Type 1: Class_Terminate Callback
VBScript存在類的概念,VBScript類支援兩個事件:Class_Initialize和Class_Terminate。在類例項化的時候會觸發Class_Initialize事件,同樣在類例項被銷燬的時候會觸發Class_Terminate事件。VBScript支援在指令碼中通過新增自定義Class_Initialize或Class_Terminate事件的方法在指令碼中手動做一些初始化或者釋放的操作。也就是說自定義Class_Terminate給了VBScript引擎執行過程一次腳本回調的機會,如果在腳本回調過程中手動修改了一些資料,就可能觸發一些未知的情形。
CASE 1:CVE-2018-1004
1.PoC
PoC執行流程:
1) 建立一個動態陣列arr
2) 建立一個MyClass物件例項並儲存到arr的第一個元素
3) 呼叫Erase函式逐個刪除arr裡的元素
4) 因為arr的第一個元素儲存了MyClass物件例項,MyClass物件例項在析構的時候會呼叫腳本里的Class_Terminate函式
5) Class_Terminate重新定義的arr
2.Debug
1)Set arr(0) = new MyClass
2)Erase a -> Class_Terminate -> ReDim Preserve a(1)
3) Erase a -> OLEAUT32!VariantClear-> crash
3.Root Cause
關於PoC的關鍵程式碼,VBScript引擎的執行過程如下圖所示:
VBScript中的陣列是由OLEAUT32的SAFEARRAY結構體定義的。
VbsErase通過呼叫OLEAUT32!_SafeArrayDestroyData釋放陣列元素,OLEAUT32!_SafeArrayDestroyData會遍歷陣列元素並將每個陣列元素的地址傳給OLEAUT32!VariantClear逐個清除陣列元素:
OLEAUT32!VariantClear中如果元素是物件,且物件的引用計數為0,則呼叫該物件的解構函式,從而觸發指令碼函式Class_Terminate的回撥。在Class_Terminate重新定義了arr的大小,導致原始arr buffer被釋放,但是OLEAUT32!VariantClear中依然保留了原始arr buffer地址,當再次訪問該地址時觸發crash。
CASE 2:CVE-2018-8174
CVE-2018-8174是360發現的一個在野0Day攻擊樣本。關於其具體利用程式碼分析可以參考360的Blog。
1.PoC
PoC執行流程:
1) 建立一個數組arr
2) 建立一個MyClass物件例項並儲存到arr的第一個元素
3) 呼叫Erase函式逐個刪除arr裡的元素
4) 因為arr的第一個元素儲存了MyClass物件例項,MyClass物件例項在析構的時候會呼叫腳本里的Class_Terminate函式
5) Class_Terminate裡將MyClass物件例項的引用儲存到變數o,並釋放自身引
6) 訪問變數o
2.Debug
1)Set arr(0) = new MyClass
2)Erase a -> Class_Terminate (轉移MyClass物件例項的引用到變數o)
3) msgbox o (通過變數o訪問MyClass物件例項)
3.Root Cause
關於PoC的關鍵程式碼,VBScript引擎的執行過程如下圖所示:
可以看到漏洞觸發的流程和CVE-2018-1004較為相似,不同的是 CVE-2018-1004是在腳本回調函式Class_Terminate中釋放了原始arr的buffer,而CVE-2018-8174則是在MyClass物件例項被析構前轉移(讀取)了MyClass物件例項的引用。
CASE 3:CVE-2018-8242
CVE-2018-8242是古河師傅分析了微軟對CVE-2018-8174的補丁後,發現的一個新的漏洞。簡單地說就是CVE-2018-8174的補丁只是禁止了在Class_Terminate中對VT_DISPATCH(Object)變數的讀操作,但是並沒有禁止在Class_Terminate中對VT_DISPATCH(Object)變數的寫操作,然而寫操作依然存在漏洞:
1.PoC
PoC執行流程:
1) 建立一個數組arr
2) 建立一個MyClass物件例項並儲存到arr的第一個元素
3) 呼叫Erase函式逐個刪除arr裡的元素
4) 因為arr的第一個元素儲存了MyClass物件例項,MyClass物件例項在析構的時候會呼叫腳本里的Class_Terminate函式
5) Class_Terminate裡再次呼叫Erase清除陣列元素
2.Debug
1)Set arr(0) = new MyClass
2)Erase a(1st) -> Class_Terminate -> Erase a(2nd)
3) Class_Terminate -> Erase a(1st)
3.Root Cause
關於PoC的關鍵程式碼,VBScript引擎的執行過程如下圖所示:
與CVE-2018-8174類似,這次在Class_Terminate再次嘗試釋放正在處於半釋放狀態下的VBScriptClass,最終觸發Double Free。
CASE 4:CVE-2018-8544
上面的3個case都與Class_Terminate中的array操作相關,而微軟在7月的補丁中直接禁止了Class_Terminate操作array,但是依然可以考慮用其他的容器代替array,實現類似的效果,比如Dictionary。
1.PoC
PoC執行流程:
1) 建立一個Dictionary物件
2) 建立一個MyClass物件例項例項並儲存到Dictionary容器,其key為“foo“
3) 重新給key-“foo“賦值,導致MyClass物件例項被釋放
4) 因為PoC中定義了MyClass的Class_Terminate函式,故MyClass物件例項在析構的時候會呼叫腳本里的Class_Terminate回撥函式
5) Class_Terminate裡呼叫Dictionary的RemoveAll函式,清空所有key-value
2.Debug
1)Set dict.Item(“foo”) = new MyClass
2)dict.Item(“foo”) = 0 -> Class_Terminate -> dict.RemoveAll
3) End Class_Terminate
3.Root Cause
關於PoC的關鍵程式碼,VBScript引擎的執行過程如下圖所示:
雖然微軟在CVE-2018-8242的補丁中徹底禁止了在Class_Terminate腳本回調中對陣列的操作:
但是仍然可以找到其他可以替代arr的容器,比如這裡的Dictionary。其漏洞原理和CVE-2018-8242相似,同樣是在Class_Terminate再次嘗試釋放(dict.RemoveAll)正在處於半釋放狀態下的VBScriptClass,最終觸發Double Free。
Type 2: Default Property Get Callback
Default Property Get用來獲取類例項的預設屬性,當嘗試將一個類例項轉換成一個字串物件時,如果指令碼中定義了Default Property Get函式,則會觸發該函式的腳本回調。與Class_Terminate回撥原理類似,如果在腳本回調Default Property Get過程中手動修改了一些資料,亦可能觸發一些未知的執行過程。
CASE 5:CVE-2018-8373
CVE-2018-8373是Trend Micro發現的另一個在野0Day攻擊樣本。關於其具體利用程式碼分析可以參考Trend的Blog。
1.PoC
PoC執行流程:
1) 建立一個數組arr
2) 建立一個MyClass物件例項並將物件的值儲存到arr的第三個元素arr(2)
3) 取MyClass物件例項的值時會觸發指令碼函式Default Property Get的呼叫
4) 回撥函式Default Property Get中重新定義的arr,導致原arr buffer被釋放
5) MyClass物件的值儲存到原arr(2)地址中
2.Debug
1)arr(2) = new MyClass: 先計算左值arr(2)地址
2)arr(2) = new MyClass: 再計算右值,觸發Default Property Get回撥,修改了arr大小
3) arr(2) = new MyClass:右值儲存到左值
3.Root Cause
關於PoC的關鍵程式碼,VBScript引擎的執行過程如下圖所示:
這次的回撥利用的是Default Property Get,可以發現其觸發流程與CVE-2018-1004類似,不同的是CVE-2018-1004是在Class_Terminate回撥中修改了arr的buffer,而CVE-2018-8373則是在Default Property Get回撥中修改了arr的buffer。
CASE 6:CVE-2018-8552
CVE-2018-8552是Google Project Zero Fuzz出來的一個漏洞,通過分析可以發現其與CVE-2018-8373相似之處。
1.PoC
PoC執行流程:
1) 建立一個數組arr,其中一個元素為MyClass物件例項
2) Filter函式用來返回一個以特定過濾條件為基礎的字串陣列的子集
3) Filter函式內遍歷到MyClass物件例項時,觸發指令碼函式Default Property Get的呼叫
4) 回撥函式Default Property Get中重新定義的arr,導致原arr buffer被釋放
2.Debug
1)arr = Array(“b”, “b”, “a”, “a”, new MyClass)
2)Call Filter(arr, “a”) -> Default Property Get -> ReDim Preserve arr(1)
3) Back Call Filter(arr, “a”)
3.Root Cause
關於PoC的關鍵程式碼,VBScript引擎的執行過程如下圖所示:
微軟在CVE-2018-8373的補丁中通過給陣列加鎖的方式禁止在類似arr(2) = new MyClass的回撥函式Default Property Get中修改陣列的大小:
但是仍然可以找到其他替代方法在回撥函式Default Property Get中修改陣列的大小來觸發漏洞,比如這裡的Filter函式。
0x02 思考
1. 指令碼中的回撥可能會導致未知的執行流程
通過上面的分析可以知道,指令碼中的回撥函式Class_Terminate,Default Property Get會打亂指令碼解析引擎的順序執行流程,並且在腳本回到解析引擎之前執行一些非常規操作,比如修改正在訪問的陣列大小導致原陣列buffer被釋放,轉移即將被釋放的物件例項,釋放即將被釋放的物件例項等等。這就容易觸發一些非預期的結果,比如UAF,OOB等等。如果能夠發現一些新的回撥方式或者觸發回撥的函式,就可能會有新的漏洞被發現。
2. 補丁分析可能會有意外的收穫
古河師傅的文章啟發我們微軟的補丁並非完美,比如有時候修復了一個數據型別的讀漏洞(CVE-2018-8174)但是可能就忽略了寫漏洞(CVE-2018-8242)。所以補丁分析對於發現一些新的漏洞是有幫助的。
這裡筆者可以給堅持閱讀到這的同學一個小彩蛋:通過對CVE-2018-8242的補丁分析知道微軟對於在Class_Terminate中運算元組引發漏洞的修補方法是在進入OLEAUT32!_SafeArrayDestroyData前將陣列的型別臨時修改為VT_EMPTY,這樣在Class_Terminate就不能運算元組了(此時陣列型別是VT_EMPTY,嘗試進行陣列的操作會觸發型別不匹配的執行時異常),最後在OLEAUT32!_SafeArrayDestroyData返回後恢復陣列的型別:
雖然不能再次在回撥函式Class_Terminate裡運算元組,但是被設定為VT_EMPTY型別的變數是否有可能被其他VARIANT再次佔用呢?如果可以被佔用,在OLEAUT32!_SafeArrayDestroyData返回後該VARIANT的型別又會被修改成陣列型別,這裡是不是就是一處型別混淆呢?:)
經試驗在筆者最新打補丁的系統是可以觸發crash的,感興趣的話也可以嘗試一下。
3. GC相關的問題仍有待發現
GC是指令碼中重要的一個特性,VBScript中主要通過引用計數方法來垃圾回收。如果引用計數錯誤的話就可能觸發引用計數的洩露問題,比如文中未提到的CVE-2018-8625。同樣可以注意到最近的兩個在野0Day: Flash CVE-2018-15982,Jscript CVE-2018-8653都是和GC相關。所以GC相關問題可能也會是後面發現漏洞的一個方向。
0x03 結論
2018年是VBScript漏洞挖掘與利用非常活躍的一年。兩個被發現的在野0Day和相當數量的CVE說明Hacker們開始關注這個即將被微軟淘汰的指令碼引擎。有理由相信,只要VBScript不被微軟從Windows中移除,接下來可能會有更多的漏洞甚至在野0Day攻擊出現。
0x04 參考文獻
- http://blogs.360.cn/post/cve-2018-8174-en.html
- http://blogs.360.cn/post/from-a-patched-itw-0day-to-remote-code-execution-part-i-from-patch-to-new-0day.html
- https://blog.trendmicro.com/trendlabs-security-intelligence/use-after-free-uaf-vulnerability-cve-2018-8373-in-vbscript-engine-affects-internet-explorer-to-run-shellcode/