{"id":"IL-0053","org":"Wiz","date":"2025-03-24","docs":[],"title":{"en":"Unauthenticated ingress-nginx admission webhook let anyone on the pod network run code and read every Secret in the cluster (IngressNightmare, CVE-2025-1974)","ko":"Ingress NGINX Controller의 어드미션 웹훅이 인증 없이 열려 있어, 조작한 Ingress 객체로 NGINX 설정을 주입해 원격 코드를 실행하고 클러스터의 모든 Secret을 읽을 수 있었다"},"domain":["kubernetes","security"],"impact":{"scope":"Ingress NGINX Controller admission webhook; about 43% of cloud environments vulnerable; more than 6,500 clusters exposed the webhook to the internet; cluster takeover and access to all Secrets","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Admission webhooks often run as web servers inside the cluster without authentication and with relatively high privileges, so any pod can reach them, and the flaws pair well with SSRF in an application. Wiz concludes that `nginx -t` should be considered harmful when it processes unsanitized user input. Wiz recommends upgrading, keeping the webhook off the internet and allowing only the API server to reach it with network policies. If upgrading must wait, disable the webhook (Helm: controller.admissionWebhooks.enabled=false; manual installs: delete the ValidatingWebhookConfiguration ingress-nginx-admission and remove --validating-webhook from the controller) and enable it again after the upgrade.","ko":"Ingress NGINX Controller를 최신 버전으로 올리고, 어드미션 웹훅 엔드포인트를 외부에 노출하지 않는다. 바로 올릴 수 없다면 네트워크 정책으로 쿠버네티스 API 서버만 어드미션 컨트롤러에 닿게 하거나 어드미션 컨트롤러를 잠시 끈다. Helm으로 설치했다면 controller.admissionWebhooks.enabled=false 로 다시 설치하고, 직접 설치했다면 ingress-nginx-admission ValidatingWebhookConfiguration을 지우고 컨트롤러의 --validating-webhook 인자를 뺀다. 업그레이드한 뒤에는 어드미션 컨트롤러를 다시 켠다.\n\n어드미션 컨트롤러는 흔히 놓치는 공격 지점이다. 인증 없이 도는 웹 서버이면서 권한이 높고, 클러스터 안 어느 파드에서나 닿는다. 공격자가 컨테이너 탈출이나 SSRF로 파드 네트워크에 들어오는 일은 생각보다 쉽다. Wiz는 최소 권한 설계가 없어 공격이 클러스터 장악까지 이어졌다고 지적했다. 걸러지지 않은 사용자 입력을 nginx -t로 검사하는 것 자체도 위험하다고 봤다. 2025년 11월 갱신된 글에 따르면 ingress-nginx는 2026년 3월에 유지보수를 끝내고 지원을 종료한다. 그러니 Gateway API나 다른 인그레스 컨트롤러로 옮겨야 한다."},"related":[],"sources":[{"url":"https://www.wiz.io/blog/ingress-nginx-kubernetes-vulnerabilities","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"To validate an Ingress, the controller's admission webhook builds a temporary NGINX configuration from a template and the Ingress object and checks it with `nginx -t`. Several values went into that file without proper sanitization: the auth-url annotation (CVE-2025-24514, not in 1.12.0, which checks annotations against strict regular expressions), the auth-tls-match-cn annotation (CVE-2025-1097, which also needs an auth-tls-secret annotation naming any TLS secret in the cluster), and the Ingress UID in the mirror parser (CVE-2025-1098). Each lets the sender inject arbitrary NGINX directives.\n\nCVE-2025-1974 turns that injection into code execution during `nginx -t`. load_module only works at the top of the file, but the ssl_engine directive of the OpenSSL module, undocumented for this use, loads a shared library from any position. To place a library on the pod, the attacker sends a request body larger than 8KB to the NGINX running in the same pod, which buffers it to a temporary file. NGINX deletes the file at once, but a Content-Length larger than the body keeps the process waiting with the file descriptor open, so the file stays reachable under ProcFS; the attacker guesses the PID and descriptor number and points ssl_engine at that path.\n\nThe code runs with the controller's service account, which by default can read Secrets in all namespaces, so the attacker can take over the cluster. The attack vector has a CVSS v3.1 base score of 9.8, and Wiz estimates about 43% of cloud environments were vulnerable. Kubernetes released internal patches for the injection flaws on 7 February 2025, removed NGINX configuration testing from the admission controller to resolve CVE-2025-1974 (reported to Wiz on 20 February), and disclosed on 24 March 2025 with fixed versions 1.12.1 and 1.11.5. A later note on the post says ingress-nginx reached end of life on 12 November 2025 and maintenance stops in March 2026.","ko":"Wiz Research는 Ingress NGINX Controller에서 인증 없이 원격 코드를 실행할 수 있는 취약점 CVE-2025-1097, CVE-2025-1098, CVE-2025-24514, CVE-2025-1974를 찾았다. Wiz 분석으로는 클라우드 환경의 약 43%가 영향을 받았고, 인터넷에 노출된 클러스터 가운데 41% 넘는 곳이 ingress-nginx를 쓰고 있었다. 어드미션 컨트롤러는 기본으로 인증 없이 네트워크로 접근할 수 있고, 클러스터 전체 Secret을 읽을 수 있는 쿠버네티스 역할로 돈다.\n\n어드미션 컨트롤러는 AdmissionReview 요청이 오면 템플릿과 Ingress 객체로 임시 NGINX 설정을 만들고 nginx -t로 검사한다. Wiz는 여기서 설정 지시어를 끼워 넣을 길을 세 가지 찾았다. CVE-2025-24514는 auth-url 어노테이션 값이 걸러지지 않고 설정에 들어가는 문제였고, 어노테이션을 정규식으로 검사하는 v1.12.0에는 해당하지 않는다. CVE-2025-1097은 auth-tls-match-cn 어노테이션의 검사를 우회하는 문제였다. 이 값이 설정에 들어가려면 auth-tls-secret 어노테이션에 클러스터 안의 TLS Secret 이름이 필요한데, ingress-nginx 서비스 계정은 모든 Secret에 접근할 수 있어서 어느 네임스페이스의 Secret이든 쓸 수 있었다. CVE-2025-1098은 어노테이션이 아닌 Ingress 객체의 UID가 mirror 설정에 그대로 들어가는 문제였다.\n\nCVE-2025-1974는 주입한 설정으로 코드를 실행하는 단계다. 코드 실행에는 설정 앞부분에서만 쓸 수 있는 load_module 대신 ssl_engine 지시어를 썼다. ssl_engine은 설정 어디서나 쓸 수 있고 공유 라이브러리를 읽어 들인다. 라이브러리 파일을 올리려고 같은 파드에서 도는 NGINX에 8KB가 넘는 요청 본문을 보냈고, NGINX는 이 본문을 임시 파일로 저장했다. NGINX는 이 파일을 곧바로 지우지만, Content-Length를 실제보다 크게 보내 연결을 붙잡아 두면 ProcFS의 파일 디스크립터로 계속 접근할 수 있다. PID와 파일 디스크립터 번호를 몇 번 추측해 이 경로를 ssl_engine에 넘기면 nginx -t가 라이브러리를 읽으면서 코드가 실행된다. 공격자는 컨트롤러 파드의 높은 권한을 얻어 모든 Secret을 읽고 클러스터를 장악할 수 있다."},"trigger":{"en":"Wiz Research reviewed the code of the Ingress NGINX admission controller and reported the first findings to Kubernetes on 31 December 2024.","ko":"인증 없는 어드미션 웹훅에 악성 Ingress 객체를 담은 AdmissionReview 요청을 보내면 공격이 시작된다. 어드미션 컨트롤러는 받은 Ingress 객체로 임시 NGINX 설정 파일을 만들고 nginx -t로 검사한다. 이때 어노테이션 값과 객체 UID가 제대로 걸러지지 않은 채 설정 파일에 들어갔다."},"severity":"unknown","symptoms":["unauthorized-access"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"Found by Wiz Research in a code review of the admission controller, not by an operator.","ko":"Wiz Research가 어드미션 컨트롤러 코드를 검토하다 찾았다. 2024년 12월 31일 CVE-2025-1974와 CVE-2025-24514를 쿠버네티스에 보고했고, 2025년 1월 2일 CVE-2025-1097을, 1월 21일 CVE-2025-1098을 보고했다. 일반 공개는 2025년 3월 24일이었다."},"components":["ingress","admission-webhook"],"root_cause":"software-bug","remediation":[{"text":{"en":"Kubernetes patched the injection flaws CVE-2025-1097, CVE-2025-1098 and CVE-2025-24514","ko":"쿠버네티스가 2025년 2월 7일 CVE-2025-1098, CVE-2025-1097, CVE-2025-24514 주입 취약점을 고친 내부 패치를 만들었다."}},{"text":{"en":"Kubernetes removed NGINX configuration testing from the ingress-nginx admission controller, resolving CVE-2025-1974","ko":"쿠버네티스가 어드미션 컨트롤러에서 NGINX 설정 검사를 빼 CVE-2025-1974를 막았고, 2025년 2월 20일 이를 Wiz에 알렸다."}},{"text":{"en":"released Ingress NGINX Controller 1.12.1 and 1.11.5","ko":"수정이 들어간 Ingress NGINX Controller 1.12.1과 1.11.5가 나왔다."}}],"action_items":[],"contributing":["default-config-unchanged","blast-radius-too-wide"],"recurrence_of":null,"symptoms_text":{"en":"This is a vulnerability report, not an outage, so nothing failed visibly. A cluster is exposed if it runs Ingress NGINX Controller older than 1.12.1 or 1.11.5 with its validating admission webhook enabled. Wiz suggests `kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx` to find it. The webhook accepts AdmissionReview requests over HTTP without authentication from any pod, and Wiz found more than 6,500 clusters exposing it to the public internet.\n\nThe exploit Wiz describes shows up as AdmissionReview requests sent straight to the webhook rather than by the API server, carrying Ingress annotations such as `nginx.ingress.kubernetes.io/auth-url: \"http://example.com/#;\\ninjection_point\"`, and as large request bodies sent to the NGINX in the same pod with a Content-Length longer than the body.","ko":"운영 중 장애로 드러난 증상은 없다. 이 사례는 Wiz Research가 Ingress NGINX Controller의 어드미션 컨트롤러 코드를 검토하다 찾은 취약점들이고, 이 취약점들을 묶어 IngressNightmare라고 부른다. CVSS v3.1 기본 점수는 9.8이다.\n\n공격은 클러스터 파드 네트워크에 닿을 수 있는 곳에서 어드미션 컨트롤러로 조작한 AdmissionReview 요청을 직접 보내는 데서 시작한다. 정상이라면 이 요청은 쿠버네티스 API 서버만 보낸다. Wiz는 이 어드미션 컨트롤러를 인터넷에 그대로 노출한 클러스터를 6,500개 넘게 찾았다. 자기 클러스터가 ingress-nginx를 쓰는지는 kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx 로 확인할 수 있다."},"updated_at":"2026-11-08T19:10:30.603338Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0053","org_slug":"wiz"}