0x01 前言
CS作為目前最流行的遠控工具,其爆出的遠端命令列漏洞CVE-2022-39197號稱指令碼小子殺手神器。之前看了@漂亮鼠大佬的文章《最新CS RCE曲折的復現路》,對文章的內容非常感興趣,文章中故意對關鍵利用鏈進行忽略,如圖1.1所示,這也勾起了大家濃烈的研究興趣。最近比較忙斷斷續續對該漏洞進行了復現,這裡想分享一些復現心得給大家。
圖1.1 大佬隱藏的關鍵利用鏈
本來以為按照作者的提示這會是比較簡單的一次復現,但是在實際過程中還是遇到很多困難,最終在二哥@gainover的幫助下完成了復現,這裡也特別感謝下二哥。
在閱讀本文之前需要首先閱讀原文,原文中很多內容已經很詳細了,我會盡量不寫原文已經有的內容,主要是分享下自己的心得體會。
0x02 初探
第一次準備尋找相應的利用鏈的時候,聽到旁邊的同事說除了CS受影響,哥斯拉也受到這個漏洞影響,我下意識的以為這是一個通用的JDK的利用鏈。所以最開始的很長一段時間我都是直接在JDK中除錯的,完全忽略了Cobalt Strike本身,雖然走了很多彎路,但是還是積累了一些基本的方法。
在第一步復現的時候,就遇到了問題。跟著作者的思路,object標籤會自動呼叫對應的setXXX方法,如圖2.1所示。我直接複製了作者給的程式碼,但是我無論如何都進不去setText方法,難道我連最基本的原理都理解錯了?
圖2.1 原文中對setXXX方法的簡單利用
仔細除錯之後才發現這裡的單詞寫錯了,不是parame,而是param。後面又在別的群裡看到了這樣的聊天記錄截圖,不得不感慨一句:“大佬套路深,我要回農村”。
圖2.2 大佬的套路
按照文章中給的思路要找到利用鏈,需要滿足四個條件。
1)classid傳入需要例項化的類,類必須繼承與Component
2)必須有無參構造方法,貌似是因為newinstant是呼叫的無參構造方法
3)必須存在一個setXXX方法的XXX屬性
4)setXXX方法的傳引數必須是接受一個string型別的引數
我們不可能人工來發現滿足條件的類和方法,可以直接透過idea發現所有繼承自Component的類,儲存類名,並透過反射的方式來篩選滿足其他條件的所有的類和方法。
從JDK中發現所有繼承自Component介面的類,如圖2.3所示。
圖2.3 JDK中繼承自Component類的類
這些類只是滿足了條件1,把所有繼承自Component類的類道出儲存成檔案。但是要滿足其他條件,還需要繼續對這些類進行篩選。為了更方便的找出滿足條件的類和方法,我寫了一個自動化遍歷的程式碼,其主要功能是讀取剛才儲存的繼承自Component類資訊的檔案,然後透過反射的方式來判斷類是否滿足其他幾個條件,如圖2.4所示。其中classFullName代表需要便利的完整的類名,主要是透過正則的方式從檔案中讀取,限於篇幅有限,就不展示了,過程中主要的步驟我都以註釋的方式解釋程式碼。
圖2.4 自動化便利篩選滿足條件的類
按照這種方式我們很容易就找到了很多滿足條件的類,如圖2.5所示。總計大概有160個。
圖2.5 在JDK中篩選的滿足條件的類及對應的方法
但是按照這種方式篩選的滿足條件的方法最終的資料量還是很大,篩選到的方法中有大量無用的方法,比如setName、setLabel、setToolTipText、setAsText等。這些方法都很簡單,一般只是對應swing最終介面展示內容的功能,我們先暫時不考慮這些功能。所以我們對上面的程式碼進行了改進,增加黑名單方法,如圖2.6所示。
圖2.6 增加黑名單方法
按照這種方式篩選之後JDK中可能滿足的方法就只剩下19個,如圖2.7所示。
圖2.7 篩選之後JDK中可疑的19個方法
這些方法都需要進行人工研判,其中最能吸引人眼球的是setDocumentBase方法,這種方法名總是給人一種像是JNDI注入設定路徑的感覺,但是實際測試來看根本就不會呼叫這個方法,因為documentBase屬性不是writeMethod,而僅僅是一個readMethod。在ObjectView類中也明確要求,滿足條件的屬性必須是writeable,而這應該也算是第5個條件吧,如圖2.8所示。
圖2.8 判斷屬性是否是屬於Writeable property
又對其他的方法依次都進行了檢視,總歸是沒有找到哪一個方法能導致命令執行。
0x03 峰轉
在我還在對著JDK的原始碼死磕的時候,看到朋友圈裡面二哥發了一張利用object標籤執行命令的圖,然後問了一下二哥“是不是setContentType”,因為站在我當時的角度,只有setContentType裡面有較複雜的邏輯,設計到了動態類載入的過程。但是二哥回覆說“不是,不要看JDK的,可以看看CS的類”。
這時候我才突然醒悟過來,這個漏洞雖然說影響了Cobalt Strike和哥斯拉,但是實際上大佬們在平時一直都是說這是一個CS RCE漏洞,並沒有人說這是JDK SWING的RCE,Cobalt Strike和哥斯拉的利用鏈根本就不通用。
趕緊把好久不用的CS掏出來,然後把對應的jar包加入liabrary。按照之前的方式來自動化發現可能利用的方法,結果如圖3.1所示。
圖3.1 在CS的jar包中發現的可能被利用的方法
按照這種方式,確實找到一些新的可能利用的方法,依次對方法進行篩選之後之後,最終我確定這裡面可能利用的方法只可能是兩個setTeamServerAlias和setURI。只有這兩個方法中有比較複雜的程式碼邏輯,並且涉及到了動態類載入相關的程式碼,但是在實際debug的時候發現CS的所有的類斷點都只能下在方法名,不能把斷點下載方法體裡面,並且跟蹤到方法名之後F7也不能進入方法體。相當於我們除錯只能到方法名,不能到方法體除錯,這是一件很痛苦的事情,如圖3.2所示。
圖3.2 在方法體中的斷點式無法進入的
仔細研究後發現CS的class檔案為了反除錯都去掉了行號,沒有行號之後就不能在方法體內部進行除錯,只能除錯到方法名,而且這種機制目前並沒有什麼好的辦法解決。
Java的程式碼都是很複雜的,內部邏輯很多,如果不能有效的除錯,特別是涉及到內部還有多執行緒併發的操作,就會顯得尤其痛苦。因為一些其他的事情,本來好多天都沒再看這個程式碼了,後來二哥又給提示說,最終的利用鏈就是setURI,並且不需要用很複雜的分析技術,只是看了一些官方的demo就復現成功了,這個意料之中又讓人驚喜的結果讓我又重拾了對這個漏洞復現的興趣。
事實上這個官方的demo我到現在都沒找到,但是卻受到了一些思路啟發,單純的看程式碼並不是最高效的解決方案,可以和一些實際demo相結合,特別是對於當前除錯有一些技術上障礙的情況下。
首先我們明確最終的利用鏈是
org.apache.batik.swing.JSVGCanvas-->setURI
這個方法的功能是設定SVG圖片的地址,遠端載入SVG圖片。熟悉前端攻防的人其實對SVG是不陌生的,這是一個經常用於特殊場景下XSS繞過的標籤。如何來利用SVG來載入執行JAVA程式碼呢?這在網上是肯定搜尋不到的,但是如何利用SVG來載入Javascript程式碼,這個是很容易找到的。畢竟java和javascript的關係好比雷鋒和雷峰塔的關係,如圖3.3所示,
圖3.3 透過SVG來載入javascript程式碼
這個程式碼很容易讓我們想到,把JS的程式碼按照ScriptEngineManager的方式轉化為java程式碼是不是可以執行呢,如圖3.4所示?
圖3.4 把JS程式碼轉化為JAVA程式碼
如果我在獨立的環境中來執行對應的程式碼,是可以正常執行,確實是可以彈出計算器的,如圖3.5所示。
圖3.5 本地透過script標籤來執行java程式碼
但是把這個程式碼放在CS的環境中來執行,卻發現並不能正常執行,如圖3.6所示。
圖3.6 在CS環境中執行上面的程式碼報錯
從上面的程式碼報錯可以看出CS環境中並沒有執行程式碼所需的javascript相關的庫,所以爆了ClassNotFoundException的異常,這樣當然是沒有辦法直接利用的。但是這個方式仍然可以幫助我們在debug的時候發現整個利用鏈中最關鍵的一個方法呼叫
org.apache.batik.bridge.BaseScriptingEnvironment類的loadScript方法,如圖3.7所示。
圖3.7 loadScripts方法載入執行程式碼
從上面程式碼的邏輯可以看出透過獲取的type來走不通的分支流程,預設為text/ecmascript。這種方式可以透過javascript庫來執行命令,但是由於預設CSjar包中並沒有javascript庫,導致這種方式並不能執行利用,但是從程式碼中我們可以發現還有一個分支流程是type=application/java-archive的情況,如果進入這個分支流程,會是一個什麼邏輯呢,如圖3.8所示
圖3.8 application/java-archive分支的程式碼載入流程
從程式碼中可以看出這裡邏輯大體上是載入一個遠端URL地址的jar包,然後loadClass載入var13對應的類。如果整個過程完成可控,那麼我們就可以透過URLClassLoader來達到RCE的效果,那麼我們整個利用鏈就活了。
跟蹤getXLinkHref來看具體的URL地址是如何取出的,如圖3.9所示。
圖3.9 獲取遠端待載入的URL地址
從圖中可以看出URL地址來自於namespaceURL為http://www.w3.org/1999/xlink的href屬性。按照相似的辦法來構造SVG檔案的內容,如圖3.10所示。
圖3.10 改造後的SVG檔案內容
在此之後我們也只是進入了對應的流程中,還需要解決幾個對應的條件判斷,如圖3.11所示。
圖3.11 要最終RCE需要滿足的條件
其中條件1是checkCompatibleScriptURL,跟進對應的方法,一直對應的跟下去,就會發現最終會進入到DefaultScriptSecurity,如圖3.12所示。其中var2和var3分別對應遠端svg檔案地址和遠端jar包地址,要求這兩個地址必須host相同,這就很好辦到了。
圖3.12 checkCompatibleScriptURL檢查的核心邏輯
條件2和條件3可以一起來看,都是檢查配置檔案META-INF/MANIFEST.MF中的配置項,我下意識的以為META-INF/MANIFEST.MF是指CS的jar包中的配置,所以一直覺得這裡的判斷是不可能繞過的,後來才知道原來這個META-INF/MANIFEST.MF是指遠端jar包中的配置檔案,和CS無關,屬於我們可控的部分,那麼整個攻擊鏈就完全打通了。我們需要修改遠端jar包中的META-INF/MANIFEST.MF檔案,配置其中Script-Handler的值為需要遠端載入的惡意類的類名,如圖3.13所示。
圖3.13 指定要遠端載入的惡意類名稱CodeSource
至於CodeSource.class檔案,則是經過編譯之後的惡意類,只需要在靜態程式碼塊或者無參建構函式中填寫對應的惡意程式碼即可,如圖3.14所示。
圖3.14 最終執行的惡意類CodeSource
最後我們完整的來總結一下整個利用過程,需要準備好惡意的SVG檔案和惡意的jar包,jar包中包含惡意類CodeSource(其他名字也可以,但是必須和配置檔案中的名字對應),放置在一臺伺服器上,惡意的jar包中需要修改META-INF/MANIFEST.MF配置檔案。最後我們來看一下執行的效果,如圖3.15所示。這裡面會有一些報錯,但是實際上不影響執行,想要不報錯把惡意類繼承ScriptHandler就可以了。
圖3.15 利用object標籤載入遠端載入惡意類
0x04 迴歸
為了保持故事的完整性,我還是繼續對CS利用部分進行了復現,這部分的內容事實上漂亮鼠大佬已經在文章中給出了,我只是簡單的說幾句。
文中給出了兩種可能的利用方式,一種是在首頁透過frame標籤來繞過117個位元組的長度限制。但是我在實際測試的過程中發現無論我怎麼寫都會在setParent方法中報異常轉換的錯,如圖4.1所示。本來想繼續死磕一下這個frame的原理,但是後來發現另一種方式直接就成功了,所以這裡暫時忽略。
圖4.1 使用farme標籤報錯
另外一種方式是透過hook windows api的方式來傳輸惡意payload,如圖4.2所示。
圖4.2 復現最終CS RCE合影留念
0x05 結論
這個漏洞號稱是指令碼小子殺手,喜歡用CS的朋友們瑟瑟發抖。復現這個漏洞也是一個很有意思的事情,讓我學到了很多關於Swing和SVG的知識,這篇文章也希望能幫助大家對此類漏洞的除錯、復現等過程有更深入的理解。
原文連結