# IngressNightmare: CVE-2025-1974 - 9.8 Critical Unauthenticated Remote Code Execution Vulnerabilities in Ingress NGINX

Unauthenticated NGINX config injection via Ingress annotations in ingress-nginx admission controller, escalated to RCE via ssl_engine loading an uploaded shared library, leaking all cluster secrets.

- Payout: Research
- Severity: critical
- Program: ingress-nginx
- Credited: Wiz Research
- Disclosed: 2025-03-24
- CVE: CVE-2025-1974, CVE-2025-24514, CVE-2025-1097, CVE-2025-1098
- Write-up quality: 9/10
- Original: https://www.wiz.io/blog/ingress-nginx-kubernetes-vulnerabilities
- Library page: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-ingress-nginx-kubernetes-vulnerabilities
- Tags: domain:cloud domain:infra impact:data-leak impact:rce kind:research platform:threat-intel sev:critical tech:kubernetes tech:nginx technique:source-review vuln:auth-bypass vuln:command-injection vuln:rce

**Target:** Kubernetes ingress-nginx admission controller webhook (port 8443) and NGINX client-body buffering
**Root cause:** Admission controller builds an NGINX config from attacker-controlled Ingress annotations/UID without sanitization and runs `nginx -t` on it; the undocumented ssl_engine directive loads arbitrary shared libraries during config test.
**Steps:**
1. Craft AdmissionReview JSON with Ingress object; inject via `nginx.ingress.kubernetes.io/auth-url: "http://example.com/#;\ninjection_point"` (CVE-2025-24514), `auth-tls-match-cn: "CN=abc #(\n){}\n }}\nglobal_injection;\n#"` plus any TLS secret name (CVE-2025-1097), or mirror UID field (CVE-2025-1098)
2. Upload shared library: send >8KB body to NGINX port 80/443 with Content-Length larger than actual body so NGINX buffers to temp file and hangs, keeping FD open
3. Guess PID and FD to locate payload at `/proc/<pid>/fd/<fd>`
4. Inject `ssl_engine /proc/<pid>/fd/<fd>;` via annotation injection in AdmissionReview POST to admission controller
5. nginx -t loads the shared library, executing attacker code in the controller pod
**How found:** Reviewed admission controller code, noticed it writes attacker-influenced Ingress-derived config to a temp file and runs `nginx -t`; hypothesized config injection via unsanitized annotation fields; then searched for a directive usable mid-config that loads code, finding undocumented ssl_engine after load_module failed due to position constraints.
**Exploitation:** Attacker with pod-network access (or SSRF) sends crafted AdmissionReview to unauthenticated admission webhook; injects ssl_engine directive pointing at a shared library uploaded via NGINX client-body buffering to a ProcFS FD path; nginx -t loads it, achieving RCE in the ingress-nginx pod which holds a service account with read access to all secrets cluster-wide, enabling cluster takeover.
**Escalation:** RCE in pod with cluster-wide secret read role → dump all secrets across namespaces → cluster takeover; pairs with SSRF or container escape for initial network access.
**Insight:** When a component shells out to a validator binary (nginx -t, config linters) on attacker-influenced input, enumerate every directive/module that can load code or files (ssl_engine, load_module) and check whether temp-file buffering plus ProcFS FD paths can supply the payload; also audit non-annotation fields like UID that bypass annotation regex validation.
