JAVA代码审计之SpEL表达式
字数 1660 2025-08-12 11:34:30

Spring Expression Language (SpEL) 表达式注入漏洞分析与防御

1. SpEL 基础

Spring Expression Language (SpEL) 是一种功能强大的表达式语言,用于在运行时查询和操作对象图。语法上类似于 Unified EL,但提供了更多特性,特别是方法调用和基本字符串模板函数。

1.1 环境配置

在 Maven 项目中引入 SpEL 依赖:

<properties>
    <org.springframework.version>5.0.8.RELEASE</org.springframework.version>
</properties>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${org.springframework.version}</version>
</dependency>

1.2 SpEL 使用流程

SpEL 表达式求值一般分为四步:

  1. 构造解析器:使用 ExpressionParser 接口表示解析器,SpelExpressionParser 是默认实现
  2. 解析表达式:使用 parseExpression 方法解析字符串表达式为 Expression 对象
  3. 构造上下文(可选):准备变量定义等上下文数据
  4. 求值:通过 getValue 方法根据上下文获得表达式值

示例代码:

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("('Hello' + ' lisa').concat(#end)");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("end", "!");
System.out.println(expression.getValue(context));

2. SpEL 的三种主要用法

2.1 注解方式

@Value("#{表达式}")
public String arg;

2.2 XML 配置方式

<bean id="Bean1" class="com.test.xxx">
    <property name="arg" value="#{表达式}">
</bean>

2.3 代码动态处理方式

@RequestMapping("/spel")
public String spel(@RequestParam(name = "spel") String spel) {
    ExpressionParser expressionParser = new SpelExpressionParser();
    Expression expression = expressionParser.parseExpression(spel);
    Object object = expression.getValue();
    return object.toString();
}

3. SpEL 语法特性

SpEL 使用 #{...} 作为定界符,支持以下操作:

  • 引用其他对象:#{car}
  • 引用其他对象的属性:#{car.brand}
  • 方法调用:#{car.toString()}
  • 属性引用也可用 $ 符号:${someProperty}

3.1 类类型表达式

使用 T(Type) 表示 java.lang.Class 实例,"Type"必须是类全限定名(java.lang 包除外):

#{T(java.lang.Math)}

可以访问类静态方法和静态字段:

#{T(java.lang.Runtime).getRuntime().exec('calc')}

4. SpEL 表达式注入漏洞

4.1 漏洞产生条件

  1. 传入的表达式未经过滤
  2. 表达式解析后调用了 getValuesetValue 方法
  3. 使用 StandardEvaluationContext(默认)作为上下文对象

4.2 漏洞示例

@GetMapping("/spel/vuln")
public String rce(String expression) {
    ExpressionParser parser = new SpelExpressionParser();
    Expression expression1 = parser.parseExpression(expression);
    Object obje = expression1.getValue();
    String obj_str = obje.toString();
    return obj_str;
}

攻击者可以构造恶意表达式实现 RCE:

http://localhost:8080/spel/vuln?expression=T(java.lang.Runtime).getRuntime().exec('calc')

5. 漏洞审计方法

  1. 全局搜索 org.springframework.expression.spel.standard
  2. 搜索 expression.getValue()expression.setValue()
  3. 定位到具体漏洞代码后,分析传入参数是否可控
  4. 追踪参数来源,确认用户输入是否未经处理直接传入

6. 修复建议

6.1 使用 SimpleEvaluationContext

SimpleEvaluationContext 仅支持 SpEL 语言语法的一个子集,不包括 Java 类型引用、构造函数和 bean 引用:

@GetMapping("/spel/sec")
public String spel_sec(String expression) {
    ExpressionParser parser = new SpelExpressionParser();
    // 只读属性
    EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
    return parser.parseExpression(expression).getValue(context).toString();
}

6.2 两种 EvaluationContext 对比

特性 SimpleEvaluationContext StandardEvaluationContext
Java 类型引用 不支持 支持
构造函数 不支持 支持
Bean 引用 不支持 支持
安全性
功能 受限 完整

7. 关键词总结

  • SpelExpressionParser:SpEL 表达式解析器
  • StandardEvaluationContext:完整功能的上下文(不安全)
  • SimpleEvaluationContext:受限功能的上下文(安全)
  • T():类类型运算符(可能导致 RCE)
  • getValue()/setValue():表达式求值方法

8. 已知相关漏洞

  • Jackson 的 CVE-2017-17485
  • WebLogic 的 CVE-2019-2725

这些漏洞都是通过反序列化让程序加载恶意构造的 XML 文件,利用 SpEL 表达式实现远程代码执行。

Spring Expression Language (SpEL) 表达式注入漏洞分析与防御 1. SpEL 基础 Spring Expression Language (SpEL) 是一种功能强大的表达式语言,用于在运行时查询和操作对象图。语法上类似于 Unified EL,但提供了更多特性,特别是方法调用和基本字符串模板函数。 1.1 环境配置 在 Maven 项目中引入 SpEL 依赖: 1.2 SpEL 使用流程 SpEL 表达式求值一般分为四步: 构造解析器 :使用 ExpressionParser 接口表示解析器, SpelExpressionParser 是默认实现 解析表达式 :使用 parseExpression 方法解析字符串表达式为 Expression 对象 构造上下文 (可选):准备变量定义等上下文数据 求值 :通过 getValue 方法根据上下文获得表达式值 示例代码: 2. SpEL 的三种主要用法 2.1 注解方式 2.2 XML 配置方式 2.3 代码动态处理方式 3. SpEL 语法特性 SpEL 使用 #{...} 作为定界符,支持以下操作: 引用其他对象: #{car} 引用其他对象的属性: #{car.brand} 方法调用: #{car.toString()} 属性引用也可用 $ 符号: ${someProperty} 3.1 类类型表达式 使用 T(Type) 表示 java.lang.Class 实例,"Type"必须是类全限定名( java.lang 包除外): 可以访问类静态方法和静态字段: 4. SpEL 表达式注入漏洞 4.1 漏洞产生条件 传入的表达式未经过滤 表达式解析后调用了 getValue 或 setValue 方法 使用 StandardEvaluationContext (默认)作为上下文对象 4.2 漏洞示例 攻击者可以构造恶意表达式实现 RCE: 5. 漏洞审计方法 全局搜索 org.springframework.expression.spel.standard 搜索 expression.getValue() 或 expression.setValue() 定位到具体漏洞代码后,分析传入参数是否可控 追踪参数来源,确认用户输入是否未经处理直接传入 6. 修复建议 6.1 使用 SimpleEvaluationContext SimpleEvaluationContext 仅支持 SpEL 语言语法的一个子集,不包括 Java 类型引用、构造函数和 bean 引用: 6.2 两种 EvaluationContext 对比 | 特性 | SimpleEvaluationContext | StandardEvaluationContext | |------|-------------------------|---------------------------| | Java 类型引用 | 不支持 | 支持 | | 构造函数 | 不支持 | 支持 | | Bean 引用 | 不支持 | 支持 | | 安全性 | 高 | 低 | | 功能 | 受限 | 完整 | 7. 关键词总结 SpelExpressionParser :SpEL 表达式解析器 StandardEvaluationContext :完整功能的上下文(不安全) SimpleEvaluationContext :受限功能的上下文(安全) T() :类类型运算符(可能导致 RCE) getValue() / setValue() :表达式求值方法 8. 已知相关漏洞 Jackson 的 CVE-2017-17485 WebLogic 的 CVE-2019-2725 这些漏洞都是通过反序列化让程序加载恶意构造的 XML 文件,利用 SpEL 表达式实现远程代码执行。