容器逃逸即集群管理員?你的集群真的安全嗎?
一、簡介
在2022年的KubeCon會議上,來自Palo Alto Networks的安全研究員Yuval Avrahami和Shaul Ben Hai分享了議題《Trampoline Pods:Node to Admin PrivEsc Built Into Popular K8s Platforms》[1] ,介紹了攻擊者在容器逃逸之后如何利用節點上“TrampolinePods”的權限來接管集群,其中涉及的技術和思路十分值得學習與思考,本文主要介紹該技術的原理和步驟,擴展了同一類型的方法,希望云安全人員在深入了解攻擊技術之后,能夠發現并緩解生產環境中存在的類似風險,共同建設云環境安全。
本文涉及到的技術僅供教學、研究使用,禁止用于非法用途。
二、事出有因
該技術的分析來源于針對惡意軟件Siloscape的分析[2] 。2021年3月,研究員第一次發現針對Windows容器的惡意軟件并將其命名為Siloscape,在還原其攻擊鏈時(圖1所示)發現Siloscape展示了一種未曾見過的在野攻擊思路:在入侵Kubernetes集群的一個節點后,它會檢查節點上是否有create Deployments的權限,如果有則在集群內創建一個Deployment后門,如果沒有則停止繼續攻擊。

圖1 Siloscape的攻擊鏈(圖片源自針對惡意軟件Siloscape的分析[2] )
該思路引發人思考:如何配置Kubernetes集群節點的權限才能防止此類攻擊?如果配置不當又會造成什么風險?帶著這兩個問題我們繼續深入其中。
三、容器逃逸的真正影響
容器技術在被廣泛應用的同時,也帶來了對應的安全問題——容器逃逸。我們在《容器逃逸技術概覽》中,系統地將容器逃逸的根源劃分為4個類型:危險配置導致的容器逃逸、危險掛載導致的容器逃逸、相關程序漏洞導致的容器逃逸、內核漏洞導致的容器逃逸,不論通過何種方式從容器逃逸到宿主機,直接的影響似乎只是控制了容器所在的宿主機。若該宿主機是Kubernetes集群的一個普通節點,從滲透測試的角度來看,下一步需要進行的便是橫向移動或權限提升。關于Kubernetes集群的權限提升,不論是CVE-2018-1002105還是CVE-2020-8559,漏洞的利用都依賴相關組件存在漏洞這個前提,倘若目標集群Kubernetes的相關組件都是安全的,如何在集群內進行權限提升呢?筆者通過整理現有的技術并類比針對容器逃逸的類型劃分,將Kubernetes集群的權限提升手法劃分為2個類型:相關程序漏洞導致的權限提升、危險的RBAC(基于角色的訪問控制)配置導致的權限提升。本文主要討論危險的RBAC配置導致的權限提升,為了更加容易理解后文涉及的技術手法,下面將介紹一些相關背景知識。
四、背景知識
4.1
DaemonSets
當希望Pod在集群中的每個節點上運行時,需要創建DaemonSet對象,如Kubernetes的kube-proxy進程,負責節點的網絡代理,需要運行在每個節點上。當有節點加入集群時,DaemonSet會為它們新增一個Pod,當節點從集群中移除時,這些Pod也會被回收。刪除DaemonSet將會刪除它創建的所有Pod。
4.2
ServiceAccount
Pod自身在訪問Kubernetes API Server時,需要使用內置的ServiceAccount(簡稱sa,下同)。sa在創建時,會在同一命名空間下生成一個與之關聯的Secret資源,Secret存儲認證所需的token、ca.crt等內容。默認情況下,Pod會自動掛載同一命名空間下的名為default的sa,相關文件掛載在Pod中容器/var/run/secrets/kubernetes.io/serviceaccount/路徑下。
五、危險的RBAC配置
“人類才是系統中最大的漏洞”,不論是傳統應用場景,還是如今的云原生場景,權限配置一直是困擾安全人員的最大挑戰之一。權限配置不當導致的安全事件比比皆是,因此誕生了各種標準來規范應用程序的權限以防止發生風險。
一般情況下,Kubernetes集群中節點上主要運行的Pod類型有以下三種,如圖2所示:

圖2 節點上運行的Pod類型
- 業務Pod
- 附加組件(Prometheus,Istio此類)
- 系統組件(Kube-proxy,coredns)
業務Pod的權限一般較小,附件組件和系統組件提供集群管理的服務,它的權限也不應等于集群管理員的權限,但在實際場景中,集群管理員可能配置不當,賦予對應角色過高的權限。當攻擊者從外部進入容器環境中并逃逸至宿主機時,往往會關注節點上Pod的權限,若發現高權限的sa,則可以憑借它完成集群內的權限提升。現根據利用功能將角色涉及的敏感權限和對應的風險進行整理,如圖3和附錄A所示:

圖3 根據利用手段劃分權限
注:
操控認證/授權:有權修改認證標識或角色權限,如escalate clusterrole
獲取憑證:有權獲取或下發憑證,如list secrets
命令執行:有權在Pod或Node上執行命令,如pods/exec
管理Pod:有權轉移Pod或更新節點,如update nodes,delete pods
中間人:有權攔截通信流量,如create endpointslices
六、攻擊案例
下面將以CNI插件Cilium為例,介紹攻擊者在容器逃逸之后,如何利用高權限的Pod從工作節點獲取集群管理員權限。
Cilium的架構主要包含Cilium Agent(簡稱Agent)和Cilium Operator(簡稱Operator)組件,如圖4所示。

圖4 Kubernetes集群中的Cilium
其中Agent的功能是接收來自上層的配置,包括通過Kubernetes或API來定義網絡、服務負載均衡、網絡策略、可見性和監控需求,它以DaemonSet形式部署,在每個節點上運行。在較早版本(v1.12.0-rc2版本之前)中的Agent內置的sa擁有集群內的delete pods權限和update nodes/status權限。
Operator的功能是管理集群,主要是節點之間資源信息的同步、確保 Pod DNS 更新管理、集群 NetworkPolicy 的管理和更新等,它以Deployment形式部署,隨機分配在集群中的某個節點上。同樣地,Operator在較早版本中內置的sa擁有集群內的list secrets權限。需要知道的是,在Kubernetes集群中,list secrets權限可以直接獲得secret的內容,官網文檔已經說明[3] ,具體效果如圖5、圖6、圖7所示:

圖5 list secrets權限示例

圖6 get secret name效果
metarget命名空間下名為default的sa擁有list secrets的權限,直接get secrets+secretname讀取會提示forbidden,但是可以改用get secret來獲取其內容,如圖7所示:

圖7 list權限獲取secret值
當獲取到Operator的sa時,可以利用它來讀取一些更高權限賬戶的secret。但當攻擊者通過一系列手段從容器逃逸至宿主機時,該宿主機可能只是集群中的一個普通節點,而Operator是以Deployment的形式隨機部署在某個節點上,并不一定會在當前節點,如圖8所示:

圖8 攻擊者入侵至普通節點
因此需要通過一定手段將Operator從其他節點轉移至當前節點,由此可將攻擊大概分為以下三個步驟:
第一步:轉移Operator
如何轉移Operator?需要利用Agent的sa。在節點上可以通過文件系統或進入容器內部獲取Agent的sa,先利用update nodes/status權限將其他所有節點的PodCapacity置為0,然后利用delete pods權限將Operator刪除。Capacity代表節點的資源容量,包括cpu、memory、將PodCapacity置為0,代表節點上Pod的容量為0。當Operator被刪除時,因為Deployments的特性,Kubernetes API Server會重新創建一個副本,在資源調度時會檢查節點上的Capacity值,當發現其他所有節點的PodCapacity值為0時,便會將Operator部署在攻擊者控制的節點上,完成轉移。
第二步:竊取憑證
當Operator可控時,同樣可以通過文件系統或進入容器的方式獲取Operator的sa。那么問題來了,當擁有讀取secret的權限時,需要讀取誰的secret才能進一步擴大權限,甚至一步到位?經過調查,發現Kubernetes集群內有這樣一個角色:clusterrole-aggregation-controller(簡稱CRAC,下同),它的權限如圖9所示:

圖9 CRAC角色權限
值得注意的是其中的“escalate”權限。一般來說,role或者clusterrole角色只能擁有在它們被創建時所賦予的權限。但escalate是個例外,它可以升級角色或集群角色的權限。為此我們需要利用Operator的sa獲取CRAC的ca.crt和secret值,通過以下命令可以獲取,如圖10所示:
curl https://server-ip:port/api/v1/namespaces/kube-system/secrets/clusterrole-aggregation-controller-token-name --header "Authorization: Bearer $token" --insecure

圖10 獲取CRAC憑證
注:其中token為Operator的secret值。
第三步:權限提升
在經過上述步驟獲取到相關憑證之后便可在從節點上進行權限提升。
使用以下命令給system:controller:clusterrole-aggregation-controller角色添加權限,效果如圖11、圖12、圖13所示:
kubectl --server=https://server-ip:port --certificate-authority=./ca.crt --token=$token edit clusterrole system:controller:clusterrole-aggregation-controller

圖11 修改權限前

圖12 修改權限

圖13 修改權限后
將權限修改為cluster-admin,即可提權至集群管理員權限。
七、方法擴展
回顧上文提到的利用Cilium在集群中的提升權限的思路,大致路線如圖14所示:

圖14 權限提升路線
觀察發現,一旦攻擊者獲取到kube-system命名空下的list secrets權限,就可以利用CRAC角色提權至集群管理員,該角色的權限是集群默認賦予的,因此在生產環境中需要控制的是list secrets權限的賦予。那么除了上述思路外,是否還有其他權限能完成攻擊鏈的構造進行權限提升?經過調研[4] [5] ,還發現下面兩種權限提升思路:
利用Node/Proxy提權
在Kubernetes的機制中,Kubelet工作在集群中的每個節點上,它負責執行來自API Server的請求并返回結果。正常情況下,訪問Kubelet API是需要憑證,但當攻擊者擁有get、create node/proxy權限時,便可以與Kubelet API直接通信,繞過API Server的訪問控制,同時因為Kubelet API不會被日志審計,也增大了檢測的難度。
攻擊者在獲取到擁有get、create node/proxy權限的secret值后,若能訪問到master節點上的Kubelet API,便可以直接與其通信,獲取到API Server的憑證,從而控制整個集群,如圖15所示:

圖15 和Kubelet API通信
利用CSR API提權
CSR即證書簽名請求,Kubernetes在多處使用客戶端證書進行認證,包括用于Kubelet和API Server之間的通信。當攻擊者擁有簽名者為kubernetes.io/kube-apiserver-client的create CSRs權限和update CSRs/approval權限時,可以為高權限的系統賬戶創建一個新的客戶端證書,用于和Kubernetes進行認證,能夠獲取到系統賬戶的權限。
其他更多思路和權限風險可以參考附錄A中提及的風險項深入挖掘。
八、思考與總結
通過對比分析不同的權限提升思路,總結了以下兩條集群內的提權路線圖:

若擁有的權限可以繞過認證,直接和API Server或Kubelet通信,便可以讀取到kube-apiserver的憑證,獲得集群管理員權限;若擁有的權限可以讀取到CRAC角色的token值,便可以通過修改角色來獲得集群管理員權限。
站在防御者的角度,高效的修復方案便是直接針對此類攻擊路線進行阻斷。在對角色的權限分配時,可以參考圖3中涉及的權限和文中提及的攻擊案例,仔細考慮每項權限的作用范圍與危害,在生產環境中遵循權限最小化原則,進行合理分配。節點之間的隔離防護,如給Kubelet服務設置防火墻,盡可能控制攻擊者的影響面。同時加強API Server的日志審計和異常檢測,對于異常的API請求應及時記錄、阻斷和警報。
本文介紹了在集群內利用危險的RBAC配置進行權限提升的思路,以此說明權限配置不當對容器逃逸后的進一步影響,希望企業的集群管理員與云廠商在管理集群環境中的角色與權限時,能夠合理分配,防范權限濫用攻擊,共同建設安全的集群環境。
附錄A
利用功能類型 |