简述
随着云原生技术的迅猛发展,其在企业数字化转型中的核心地位日益凸显,与此同时,云原生安全问题也受到了前所未有的关注。随着云原生架构的复杂度提升,漏洞的利用不再局限于单一组件的孤立缺陷,而是逐渐演变为多系统、多组件间的协同性风险
背景知识补充
通过背景知识补充,从全局视角了解风险
- ingress 是什么,和云原生什么关系,有什么特点
- ingress-nginx 与 ingress 有什么关系
- ingress-nginx 组件介绍
- 进行基本的使用和配置来了解组件如何使用
ingress 是什么
Ingress 是 Kubernetes 中的一种 API 资源,用于定义外部访问集群内服务的规则集合(主要针对 HTTP/HTTPS 流量)。它本身不直接处理流量,而是通过声明式规则描述 “如何将外部请求转发到集群内的服务”
简单概括就是 Ingress 是云原生应用暴露的 “标准化入口”
ingress 和 云原生什么关系
Ingress 作为 Kubernetes 官方定义的 API 资源 ,专门用于管理集群外部访问规则(如基于域名、路径、SSL 证书的路由)
Ingress 是必要组件么?
Kubernetes 中暴露服务的方式有多种(如 NodePort
、LoadBalancer
、ClusterIP
+ 端口转发等),Ingress 是其中一种更灵活的方案。是否需要 Ingress 取决于业务场景:
- 若仅需简单暴露单个服务(如开发环境临时访问),
NodePort
或LoadBalancer
(云环境)已足够,无需 Ingress; - 若需 多服务统一入口 (如通过同一域名 / IP 访问多个服务)、 基于路径 / 域名的精细路由 (如
api.example.com
转发到 API 服务,web.example.com
转发到前端服务)、 SSL 证书管理 、重定向 / 重写等复杂需求,Ingress 是更优选择。
特点
云原生强调 “声明式配置、动态适配、无状态化”,Ingress 完全符合这些特性:
- 声明式定义 :Ingress 通过 YAML 配置文件声明流量规则(如 “
example.com/api
路由到api-service
”),Kubernetes 控制器(如 Ingress-nginx)会自动同步规则并生效,无需手动操作,适配云原生 “基础设施即代码” 理念。 - 动态适配 Pod 变化 :当后端 Pod 因扩缩容、故障重建而 IP 变化时,Ingress 控制器会通过 Service 自动感知后端端点变化,无需修改路由规则,适配云原生应用的动态性。
- 与云原生生态工具集成 :Ingress 可与服务网格(如 Istio,通过 Gateway 资源扩展)、监控工具(如 Prometheus 采集流量 metrics)、日志系统(如 Fluentd 收集访问日志)等云原生组件无缝协作,形成完整的流量治理闭环。
ingress-nginx 与 ingress 有什么关系
Ingress 控制器是 Kubernetes 中负责解析和执行 Ingress 资源规则的组件,不同实现基于不同的代理 / 网关技术,ingress-nginx 属于 其中的一种技术
- ingress-nginx 基于 Nginx 反向代理实现的 Ingress 控制器,是 Kubernetes 社区最广泛使用的实现之一。轻量、稳定,支持基本的 HTTP/HTTPS 路由、SSL 终止、路径重写、负载均衡等功能,配置方式贴近 Nginx 原生语法。
- Traefik 一款云原生的反向代理和负载均衡器,原生支持 Kubernetes Ingress,特点是 动态配置自动发现 (无需重启即可加载新规则),集成了监控、TLS 自动配置(Let’s Encrypt)等功能,适合动态性强的环境。
- HAProxy Ingress 基于 HAProxy 实现的 Ingress 控制器,以高性能和稳定性著称,支持更复杂的负载均衡策略(如源地址哈希、权重分配),适合对性能要求较高的场景。
- Kong Ingress Controller 基于 Kong 网关(OpenResty 扩展)实现,除了基本的 Ingress 功能,还提供 API 管理能力 (如限流、认证、监控),适合需要对 API 进行精细化管控的场景。
- 云厂商专属实现
- AWS ALB Ingress Controller:将 Ingress 规则转换为 AWS Application Load Balancer(ALB)的配置。
- GKE Ingress Controller:Google Kubernetes Engine 内置的 Ingress 控制器,基于 GCP 的负载均衡服务。
- Azure Application Gateway Ingress Controller:对接 Azure 的 Application Gateway。
- 其他实现 如 Nginx Ingress Controller(与 ingress-nginx 类似,但维护方不同)、Ambassador(基于 Envoy)、Contour(基于 Envoy)等。
Kong 和 envoy 以前都通过漏洞或多或少的了解过一些
ingress-nginx 组件介绍
ingress-nginx 主要包含以下几个核心组件:
- Controller(控制器)
这是最核心的组件,负责监听 Kubernetes Ingress 资源的变化,并动态生成和管理 NGINX 配置,实现流量的路由和负载均衡。
- Admission Webhook(准入控制器)
用于在 Ingress 资源创建或更新时进行校验和默认值填充,保证配置的正确性和安全性。通常以 sidecar 或独立 Deployment 方式运行。
- Default Backend(默认后端)
当请求未命中任何 Ingress 规则时,流量会被转发到 default backend,通常用于返回 404 或自定义错误页面。
- Admin API(管理接口)
某些版本/部署方式下,ingress-nginx 提供了 admin API,用于动态管理、查询和调试 NGINX 状态(如 /healthz、/metrics、/nginx_status 等)。
- Metrics Exporter(指标导出器)
集成了 Prometheus 格式的 metrics endpoint,便于监控 NGINX 的流量、状态和性能。
- Cert-Manager Webhook(证书管理集成)
用于自动化 TLS 证书的申请、续期和挂载,通常与 cert-manager 配合使用。
- Lua/Plugin 扩展
支持通过 Lua 脚本或插件机制扩展 NGINX 的功能,比如自定义认证、日志、流量控制等。
- 工具/辅助组件
- kubectl 插件(如 charts/ingress-nginx/cmd/plugin/):用于命令行管理和调试。
- 调试工具(如 cmd/dbg/):便于开发和排查问题。
- 配置生成器(如 cmd/annotations/):自动生成注解文档或配置。
漏洞摘要说明
漏洞 CVE-2025-1974是由Wiz团队发现的一个Ingress-nginx组件漏洞,CVSS评分高达9.8。用户在默认配置情况下,拥有pod权限可访问到ingress nginx 的webhook 逻辑,在无需ingress nginx 授权情况下可触发恶意指令,导致任意代码执行
- 补丁 commit https://github.com/kubernetes/ingress-nginx/commit/626305229ffa800e226f33f1396bcb164be099ed
- 存在风险的版本
- < v1.12.1
- < v1.11.5
漏洞分析
- 组件框架逻辑
- 任意文件上传
- ingress 配置注入
组件框架逻辑
这里可以看懂为什么可以未授权访问webhook服务
ingress-nginx 是由go 语言开发。内部多个端口均使用 net/http 做为基本的web框架,准确的说该pod内起了多个web服务
端口说明 配置
内部管理端口
-
健康检查端口 - 默认 10254
- 用于健康检查端点
- 可通过 –healthz-port 参数配置
-
状态端口 - 默认 10246
- 用于 Lua HTTP 端点配置
- 可通过 –status-port 参数配置
-
流端口 - 默认 10247
- 用于 Lua TCP/UDP 端点配置
- 可通过 –stream-port 参数配置
-
默认服务器端口 - 默认 8181
- 用于暴露默认服务器(catch-all)
- 可通过 –default-server-port 参数配置
-
性能分析端口 - 默认 10245
- 用于 Go 性能分析器
- 可通过 –profiler-port 参数配置
Webhook 相关端口
- Admission Webhook 端口 - 默认 8443
- 用于 Kubernetes 准入控制器 webhook
- 在 Helm chart 中通过 controller.admissionWebhooks.port 配置
deploy.yaml 配置
在ingress-nginx 提供的多个deploy.yaml 都声明了 Service ingress-nginx-controller-admission,其中有个port 映射关系为 https-webhook
端口 443 ,映射到容器内部的webhook 端口
所以访问 https://ingress-nginx-controller-admission.ingress-nginx.svc:443 能够访问到webhook 端口
webhook 服务
在 main 函数中有一段 webhook 启动函数
mc.Start(conf.ValidationWebhook)
默认配置如下
处理逻辑见 main.go#HandleAdmission
函数
目标意图只有一个 ia.Checker.CheckIngress(&ingress)
检测ingress 规则。
- 在这里只是检测规则,所以可未授权访问
- nginx controller 会一直监听 k8s api server 中 ingress 规则的实现,修改ingress 规则。所以能够修改ingress 规则,同理也可以攻击ingress nginx 服务
任意文件上传
任意文件上传利用了linux 临时文件特性
nginx 缓存
Nginx 的
client_body_buffer_size
指令用于设置客户端请求体(如 POST 数据)的内存缓冲区大小。
当请求体大小 > 缓冲区大小时:超出部分会写入临时文件,存储路径由 client_body_temp_path 指令指定(若未配置则使用默认路径)。
以前看过缓存命名
http {
client_body_temp_path /data/nginx/tmp/body 1 2;
# 最后两个数字表示子目录层级(1级目录+2级子目录,用于分散文件)
}
从nginx缓存角度出发,这是一个难以被遍历/猜测/枚举的值
当请求体需写入临时文件时,Nginx 生成的临时文件命名遵循固定格式:
[数字].[进程ID].[序列号]
例如:0000000001.0000000123.0000000456
第一部分:请求相关的编号(递增)
第二部分:Nginx 工作进程的 PID(进程 ID)
第三部分:临时文件的序列号(同一请求可能拆分多个文件时使用)
这些文件存储在 client_body_temp_path 配置的目录及其子目录中(根据配置的层级分散存储,如 …/body/1/23/0000000001…)
linux 文件描述符
在 Linux 系统中,/proc/self/fd/
目录下的文件描述符(FD)命名规则非常简单直接,遵循以下特性:
- 命名格式 :
以纯数字作为文件名,直接对应进程持有的文件描述符编号。
例如:
/proc/self/fd/0
、/proc/self/fd/1
、/proc/self/fd/5
等。 - 编号规则 :
- 文件描述符是从
0
开始的非负整数,按进程打开文件的顺序递增分配。 - 系统默认占用前 3 个描述符:
0
:标准输入(stdin)1
:标准输出(stdout)2
:标准错误(stderr)
- 后续打开的文件(包括普通文件、管道、网络套接字等)会依次分配
3
、4
、5
…… 等编号。
- 本质是符号链接 : 这些数字文件名实际上是符号链接,指向该描述符对应的实际文件或资源。例如:
lrwx------ 1 root root 64 7月 26 10:00 5 -> /var/lib/nginx/body/0000000001.0000000123.0000000456
表示描述符5
指向 Nginx 生成的某个客户端请求体临时文件。
- 动态性 :
当文件被关闭后,对应的描述符编号会被释放,可被后续打开的文件重复使用(但不会自动删除
/proc/self/fd/
中的符号链接,而是显示为(deleted)
)。
总结来说,/proc/self/fd/
下的文件描述符命名仅以数字编号标识,与文件本身的名称、类型无关,仅反映进程内部对文件的引用编号。
这里就存在爆破的可能性了
维持长时间http连接
在这里通过将 Content-Length
设置大于原文件大小的数值,并设置 Content-Type
为流式数据
上述命令中,设置了
Content-Length
为大于原文件大小的数值,并设置Content-Type
为流式数据,这样nginx就会持续等待发送更多数据,从而导致进程挂起,文件描述符fd持续处于开启状态。
TODO
从漏洞利用角度来说这里很精彩,但需要注意一个点
- 文件上传缓存是通过nginx 完成
- 配置文件加载触发是由ingress-nginx-controller 完成
属于不同的进程,所以没法使用 /proc/self/fd
, 还需要枚举进程号
再关注其他中间件 与 linux。也存在一些临时文件及缓存处理。
注意生成缓存的进程,对于php 与 nginx 配合组合,常因为进程权限不同无法加载。但对于tomcat ,直接利用中间件缓存 /proc/self
可以避免权限问题
参考文章
- https://www.anquanke.com/post/id/264821
- https://www.leavesongs.com/PENETRATION/springboot-xml-beans-exploit-without-network.html
- 从JDBC MySQL不出网攻击到spring临时文件利用-先知社区
配置注入
这里使用了go 配置框架 text/template
模板
上手写了下demo 测试
func dollarVariableTemplateExample() {
fmt.Println("6. $变量作用域示例:")
type Server struct {
ServerName string
Locations []*Location
}
apiRaw := "http://auth-service:3000/verify#;}}}\n\nssl_engine /test/custom_path;\n\n"
adminRaw := "http://admin-auth:3001/verify"
server := Server{
ServerName: "example.com",
Locations: []*Location{
{
Path: "/api",
ServiceName: "api-service",
ServicePort: 8080,
ExternalAuth: &ExternalAuth{
URL: apiRaw,
Method: "api-host",
},
}
},
}
tmplText := `
server {
server_name ;
location {
proxy_pass http://:;
# 外部认证配置
set ;
set ;
}
}`
tmpl, err := template.New("dollar").Funcs(template.FuncMap{
"quote": quote,
}).Parse(tmplText)
if err != nil {
fmt.Printf("模板解析错误: %v\n", err)
return
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, server)
if err != nil {
fmt.Printf("模板执行错误: %v\n", err)
return
}
fmt.Println(buf.String())
fmt.Println()
}
可以发现通过 双引号 闭合导致模板注入发生。(这里我理解是go语言模板库未提供安全框架)
修复使用自定义 quote 函数 和 strconv.Quote 进行转义
在漏洞利用处,通过配置注入,注入 ssl_engine 缓存so 文件,利用 nginx 配置测试时 ,加载恶意so文件,造成命令执行
修复
修复
整个漏洞完整的补丁见
commit https://github.com/kubernetes/ingress-nginx/commit/626305229ffa800e226f33f1396bcb164be099ed
最重要的修复是临时删掉了 nginx 配置测试加载,很干脆。
/* Deactivated to mitigate CVE-2025-1974
// TODO: Implement sandboxing so this test can be done safely
err = n.testTemplate(content)
if err != nil {
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
return err
}
*/
后利用问题,
ingress-nginx 具备 clusterRole 权限具备所有配置可读权限。
# ClusterRole 权限
- apiGroups: [""]
resources: ["configmaps", "endpoints", "nodes", "pods", "secrets", "namespaces"]
verbs: ["list", "watch"]