کابوس اینگرس: آسیبپذیریهای بحرانی ۹.۸ در اجرای کد از راه دور بدون احراز هویت در اینگرس NGINX

بیش از ۴۰٪ از محیطهای ابری در معرض آسیبپذیری بحرانی اینگرس NGINX اجرای کد از راه دور (RCE) قرار دارند که احتمالاً منجر به تصاحب کامل کلاستر خواهد شد.
پژوهشگران Wiz Research مجموعهای از آسیبپذیریهای اجرای کد از راه دور (RCE) بدون احراز هویت را در Ingress NGINX Controller برای Kubernetes کشف کردهاند که با نام #IngressNightmare شناخته میشود. این آسیبپذیریها شامل CVE-2025-1097، CVE-2025-1098، CVE-2025-24514 و CVE-2025-1974 هستند.
سوءاستفاده از این ضعفهای امنیتی به مهاجمان امکان میدهد تا بدون مجوز به تمامی (Secrets) ذخیرهشده در تمامی Namespaceهای یک کلاستر Kubernetes دسترسی پیدا کنند که میتواند به تصاحب کامل کلاستر منجر شود.
این حمله با امتیاز خطر ۹.۸ (طبق استاندارد CVSS v3.1) ارزیابی شده است.
در این پست وبلاگ، ما یافتههای کلیدی خود از کشف IngressNightmare را به اشتراک میگذاریم؛ آسیبپذیریای که کامپوننت Admission Controller در Ingress NGINX Controller برای Kubernetes را تحت تأثیر قرار میدهد.
بر اساس تحلیل ما، حدود ۴۳٪ از محیطهای ابری در برابر این آسیبپذیریها آسیبپذیر هستند. تحقیقات ما نشان داده است که بیش از ۶۵۰۰ کلاستر Kubernetes، از جمله کلاسترهای متعلق به شرکتهای Fortune 500، بهطور عمومی Admission Controller آسیبپذیر Ingress NGINX را در اینترنت در معرض دید قرار دادهاند—و این موضوع آنها را در معرض خطر بحرانی و فوری قرار میدهد.
توصیه میکنیم هرچه سریعتر نسبت به رفع این آسیبپذیری اقدام کنید.
این مطلب به تشریح جنبههای فنی این حفره امنیتی پرداخته و شامل راهکارهای مقابله و راهنمای تشخیص برای تیمهای دفاعی میباشد.
مایلیم از تیم نگهداری Ingress-NGINX، بهویژه Marco Ebert، برای همکاری در رفع آسیبپذیریهای IngressNightmare تشکر کنیم. تیم ما بهطور نزدیک با توسعهدهندگان Kubernetes و تیمهای امنیتی همکاری کرد تا اطمینان حاصل شود که این سطح حمله پیش از افشای عمومی بهطور کامل برطرف شده است.
وبلاگ Kubernetes در اینجا در دسترس است، و همچنین Amazon و Google Cloud نیز توصیهنامههای امنیتی خود را منتشر کردهاند.
فهرست مطالب
Ingress NGINX Controller برای Kubernetes چیست؟
Ingress NGINX Controller یکی از محبوبترین Ingress Controllerها برای Kubernetes محسوب میشود و یکی از پروژههای اصلی Kubernetes است که بیش از ۱۸,۰۰۰ ستاره در GitHub دارد.
استفاده از Ingress-NGINX یکی از رایجترین روشها برای در معرض قرار دادن اپلیکیشنهای Kubernetes به صورت عمومی است. این کنترلر وظیفه دارد ترافیک ورودی را دریافت کرده و بر اساس مجموعهای از قوانین، آن را به سرویسهای Kubernetes هدایت کند. این سرویسها نیز ترافیک را به Podsهای مناسب منتقل میکنند.
بهطور خاص، Ingress NGINX Controller بر پایه پروکسی معکوس NGINX ساخته شده است که یکی از پرکاربردترین ابزارهای مدیریت ترافیک در دنیای سرور و شبکه محسوب میشود.

آسیبپذیری IngressNightmare در Ingress NGINX
Ingress-NGINX بهطور رسمی در مستندات Kubernetes بهعنوان یک Ingress Controller نمونه معرفی شده که پیشنیاز استفاده از Ingress در Kubernetes را برآورده میکند. تحقیقات ما نشان میدهد که بیش از ۴۱٪ از کلاسترهای متصل به اینترنت در حال اجرای Ingress-NGINX هستند.
جزئیات آسیبپذیری
Ingress NGINX درون Pod خود یک Admission Controller اجرا میکند که برای اعتبارسنجی (Validation) آبجکتهای Ingress قبل از استقرار آنها طراحی شده است. مشکل اصلی اینجاست که Admission Controllerها بهطور پیشفرض بدون احراز هویت از طریق شبکه قابل دسترسی هستند و این موضوع آنها را به یک بردار حمله بسیار جذاب برای مهاجمان تبدیل میکند.
هنگامی که Admission Controller یک Ingress Object جدید را پردازش میکند، یک پیکربندی NGINX از آن میسازد و سپس با استفاده از باینری NGINX آن را اعتبارسنجی میکند. تیم ما در این فرایند آسیبپذیریای کشف کرد که امکان تزریق یک پیکربندی دلخواه NGINX از راه دور را فراهم میکند. مهاجم میتواند با ارسال یک Ingress Object مخرب مستقیماً به Admission Controller از طریق شبکه، این حمله را انجام دهد.
در مرحله اعتبارسنجی پیکربندی، پیکربندی مخرب باعث اجرای کد درون NGINX Validator میشود، که در نهایت اجازه اجرای کد از راه دور (RCE) روی Pod کنترلر Ingress NGINX را به مهاجم میدهد.
مجوزهای سطح بالای Admission Controller و دسترسی نامحدود آن از طریق شبکه، یک مسیر بحرانی برای افزایش سطح دسترسی ایجاد میکند.
سوءاستفاده از این نقص به مهاجم امکان میدهد کد دلخواه اجرا کرده و به تمامی Secrets کلاستر در تمامی Namespaceها دسترسی پیدا کند، که میتواند در نهایت به تصاحب کامل کلاستر Kubernetes منجر شود.

دستیابی اولیه به شبکه Podهای یک کلاستر آسانتر از آن چیزی است که تصور میشود
برخلاف تصور رایج، دسترسی اولیه به شبکه Podهای یک کلاستر چندان دشوار نیست—زیرا خود کانتینرسازی یک مرز امنیتی قوی محسوب نمیشود. بسیاری از اپلیکیشنهایی که روی Kubernetes اجرا میشوند، در برابر حملات Container Escape آسیبپذیر هستند. این موضوع را ما بارها در تحقیقات خود درباره اپلیکیشنهای ابری و SaaS در سالهای اخیر نشان دادهایم.
علاوه بر این، این آسیبپذیریها بهخوبی با حملات SSRF (Server-Side Request Forgery) ترکیب میشوند، که یکی از رایجترین آسیبپذیریها در اپلیکیشنهای وب محسوب میشود.
راهکارهای کاهش خطر و شناسایی تهدید
ابتدا بررسی کنید که آیا کلاستر شما از ingress-nginx استفاده میکند. در بیشتر موارد، میتوانید این کار را با اجرای دستور زیر انجام دهید:
kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx
توجه: برای اجرای این دستور حداقل به مجوز خواندن در سطح کلاستر (Read-Only) نیاز دارید.
این آسیبپذیری در نسخههای 1.12.1 و 1.11.5 از Ingress NGINX Controller برطرف شده است. بهشدت توصیه میکنیم که مدیران کلاستر اقدامات زیر را انجام دهند:
- Ingress NGINX Controller را به آخرین نسخه موجود بهروزرسانی کنید.
- مطمئن شوید که EndPoint مربوط به Admission Webhook بهصورت عمومی در دسترس نیست.
- میتوانید از Nuclei template برای شناسایی Admission Controllerهای آسیبپذیر استفاده کنید.
اگر امکان بهروزرسانی فوری وجود ندارد، میتوانید یکی از راهکارهای کاهش خطر زیر را در نظر بگیرید:
- سیاستهای سختگیرانه شبکه را اعمال کنید تا فقط Kubernetes API Server به Admission Controller دسترسی داشته باشد.
- موقتاً کامپوننت Admission Controller را غیرفعال کنید (در صورت عدم امکان ارتقا):
- اگر ingress-nginx را با Helm نصب کردهاید، آن را با این تنظیمات دوباره نصب کنید:
controller.admissionWebhooks.enabled=false
- اگر ingress-nginx را بهصورت دستی نصب کردهاید،ValidatingWebhookConfiguration مربوط به ingress-nginx-admission را حذف و همچنین آرگومان
--validating-webhook
را از Deployment کانتینتر یا DaemonSet مربوط بهingress-nginx-controller
حذف کنید.
توجه: پس از ارتقا به نسخه ایمن، حتماً Admission Controller را دوباره فعال کنید، زیرا نقش مهمی در تأمین امنیت پیکربندیهای Ingress دارد.
اگر میخواهید سیستم شناسایی آسیبپذیری IngressNightmare را آزمایش کنید، میتوانید از Terraform برای راهاندازی یک کلاستر عمداً آسیبپذیر استفاده کنید.
git clone https://github.com/ofirc/ingress-nightmare.git cd ingress-nightmare terraform init terraform plan terraform apply -auto-approve kubectl port-forward svc/ingress-nginx-controller-admission -n ingress-nginx 8443:443 curl -vk https://localhost:8443/validate -H \"Content-Type: application/json\" --data-binary @admission-review.json
توجه داشته باشید که CVE-2025-24513 از نظر ماهیت با سایر آسیبپذیریهای موجود در زنجیره IngressNightmare متفاوت است، زیرا این آسیبپذیری به اجرای کد از راه دور (RCE) منجر نمیشود.
چگونه IngressNightmare را کشف کردیم؟
انگیزه تحقیق
Admission Controllers کوبنتیز یک سطح حمله جالب و اغلب نادیده گرفتهشده در محیط کوبنتیز ارائه میدهند. این کنترلرها توسط سرور API کوبنتیز فعال میشوند تا درخواستهای (AdmissionReview) را پیش از پردازش بررسی کرده و در صورت لزوم تغییر دهند یا مسدود کنند، و اغلب با امتیازات نسبتاً بالایی در داخل کلاستر اجرا میشوند. Admission Controllers معمولاً نیازی به احراز هویت ندارند و اساساً بهعنوان سرورهای وب عمل میکنند، که این موضوع یک نقطه پایانی اضافی قابلدسترس در شبکه داخلی کلاستر ایجاد میکند. این معماری به مهاجمان اجازه میدهد تا از هر پاد (pod) در شبکه مستقیماً به آنها دسترسی پیدا کنند، که بهطور قابلتوجهی سطح حمله را افزایش میدهد.
پیشزمینهای درباره کنترلر اینگرس NGINX برای کوبنتیز
کنترلر اینگرس NGINX یک implementation اینگرس است که از NGINX بهعنوان یک reverse proxy و load balancer استفاده میکند. این یکی از محبوبترین پروژهای محوری در کوبنتیز است.
برای ایجاد bridge بین کوبنتیز و کانفیگهای NGINX، که یک فناوری غیربومی کوبنتیز (non-Kubernetes-native) است، این کنترلر تلاش میکند Ingress objects کوبنتیز را به پیکربندیهای NGINX ترجمه کند. برای اطمینان از پایداری سرور NGINX، کنترلر از یک وبهوک اعتبارسنجی (validating admission webhook) استفاده میکند که پیکربندی نهایی را پیش از اعمال، اعتبارسنجی میکند.

از دیدگاه یک مهاجم، Admission Controller یک نقطه پایانی HTTP بدون احراز هویت است که مسئول انجام عملیات پیچیده است و بهطور پیشفرض با نقشی در کوبنتیز اجرا میشود که اجازه دسترسی به تمامی environment’s secrets را میدهد، و همین موضوع آن را به هدفی جذاب برای تحقیق تبدیل میکند.
Injection پیکربندی NGINX بصورت ریموت از راه دور
در جریان بررسی Ingress NGINX Admission Controller code، ما یک path جالب را شناسایی کردیم: هنگامی که این کنترلر درخواستهای AdmissionReview دریافتی را پردازش میکند، بر پایه template file و Ingress object یک فایل پیکربندی موقت NGINX را تولید میکند. سپس با استفاده از دستور nginx -t
، اعتبار فایل پیکربندی موقت را آزمایش میکند. ما چندین روش برای تزریق دستورات پیکربندی جدید در این code path پیدا کردیم.
// testTemplate checks if the NGINX configuration inside the byte array is valid // running the command "nginx -t" using a temporal file. func (n *NGINXController) testTemplate(cfg []byte) error { ... tmpfile, err := os.CreateTemp(filepath.Join(os.TempDir(), "nginx"), tempNginxPattern) ... err = os.WriteFile(tmpfile.Name(), cfg, file.ReadWriteByUser) ... out, err := n.command.Test(tmpfile.Name()) func (nc NginxCommand) Test(cfg string) ([]byte, error) { //nolint:gosec // Ignore G204 error return exec.Command(nc.Binary, "-c", cfg, "-t").CombinedOutput() }
بهطور معمول، فقط سرور API کوبنتیز باید این درخواستهای AdmissionReview را ارسال کند. با این حال، از آنجا که Admission Controller فاقد احراز هویت است، یک مهاجم با دسترسی حداقلی به شبکه میتواند درخواستهای AdmissionReview دلخواه را از هر پاد (pod) درون کلاستر بسازد و ارسال کند.
برای آزمایش خود، ما از kube-review
استفاده کردیم تا درخواستهای بررسی پذیرش را از مانیفستهای منبع اینگرس ایجاد کنیم، که سپس میتوانستند بهصورت مستقیم از طریق HTTP به admission controller ارسال شوند.
{ "kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1", "request": { "uid": "732536f0-d97e-4c9b-94bf-768953754aee", ... "name": "example-app", "namespace": "default", "operation": "CREATE", ... "object": { "kind": "Ingress", "apiVersion": "networking.k8s.io/v1", "metadata": { "name": "example-app", "namespace": "default", ... "annotations": { "nginx.ingress.kubernetes.io/backend-protocol": "FCGI" } }, "spec": { "ingressClassName": "nginx", "rules": [ { "host": "app.example.com", "http": { "paths": [ { "path": "/", "pathType": "Prefix", "backend": { "service": { "name": "example-service", "port": {} } } } ] } } ] }, ... } }
همانطور که در بالا مشاهده میشود، فیلدهای زیادی وجود دارند که ما میتوانیم آنها را کنترل کنیم، که این نشاندهنده سطح حمله گسترده است.

با توجه به اینکه اصطلاحات فنی را نمیتوان به فارسی ترجمه کرد، در ادامه گزارش فنی را به زبان اصلی قرار میدهیم.
CVE-2025-24514 – auth-url Annotation Injection
func (a authReq) Parse(ing *networking.Ingress) (interface{}, error) { // Required Parameters urlString, err := parser.GetStringAnnotation(authReqURLAnnotation, ing, a.annotationConfig.Annotations) if err != nil { return nil, err }
When the temporary configuration is created, $externalAuth.URL—which corresponds to the URL from the auth-url annotation—is incorporated without proper sanitization.
proxy_http_version 1.1; proxy_set_header Connection ""; set $target {{ changeHostPort $externalAuth.URL $authUpstreamName }}; {{ else }} proxy_http_version {{ $location.Proxy.ProxyHTTPVersion }}; set $target {{ $externalAuth.URL }}; {{ end }}
This lack of proper sanitization allows an attacker to inject arbitrary NGINX configuration directives, which get evaluated when nginx -t runs.
Consider the following auth-url annotation:
nginx.ingress.kubernetes.io/auth-url: "http://example.com/#;\ninjection_point"
The final configuration will appear as follows
... proxy_http_version 1.1; set $target http://example.com/#; injection_point proxy_pass $target; ...
This vulnerability does not apply to v1.12.0. In this version, Ingress NGINX Controller changed its default security settings to verify all annotations, including auth-url, against strict regex rules.
CVE-2025-1097 – auth-tls-match-cn Annotation Injection
The authtls parser, for its auth-tls-match-cn annotation, uses CommonNameAnnotationValidator to validate the field value:
func CommonNameAnnotationValidator(s string) error { if !strings.HasPrefix(s, "CN=") { return fmt.Errorf("value %s is not a valid Common Name annotation: missing prefix 'CN='", s) } if _, err := regexp.Compile(s[3:]); err != nil { return fmt.Errorf("value %s is not a valid regex: %w", s, err) } return nil }
In other words, the auth-tls-match-cn annotation requires
The value must start with CN=.
All remaining characters must form a valid regular expression.
Similar to the previous injection, $server.CertificateAuth.MatchCN
corresponds to the value of the auth-tls-match-cn
annotation. While tricky, we can still bypass both requirements to inject arbitrary NGINX configurations in this part of the template
if ( $ssl_client_s_dn !~ {{ $server.CertificateAuth.MatchCN }} ) { return 403 "client certificate unauthorized"; }
Consider the following auth-tls-match-cn
annotation
nginx.ingress.kubernetes.io/auth-tls-match-cn: "CN=abc #(\n){}\n }}\nglobal_injection;\n#"
The final configuration will appear as follows
... set $proxy_upstream_name "-"; if ( $ssl_client_s_dn !~ CN=abc #( ){} }} global_injection; # ) { return 403 "client certificate unauthorized"; } ...
For the auth-tls-match-cn
annotation value to appear in the configuration, we also need to provide the nginx.ingress.kubernetes.io/auth-tls-secret
annotation, which corresponds to a TLS certificate or keypair secret present in the cluster. Since the service account used by Ingress NGINX has access to all secrets in the cluster, we can specify any secret name from any namespace, provided it matches the required TLS certificate/keypair format. Notably, many managed Kubernetes solutions include such secrets by default. Below is a short list of common secrets that can be leveraged in this type of attack
kube-system/konnectivity-certs kube-system/azure-wi-webhook-server-cert kube-system/aws-load-balancer-webhook-tls kube-system/hubble-server-certs kube-system/cilium-ca calico-system/node-certs cert-manager/cert-manager-webhook-ca linkerd/linkerd-policy-validator-k8s-tls linkerd/linkerd-proxy-injector-k8s-tls linkerd/linkerd-sp-validator-k8s-tls
CVE-2025-1098 – mirror UID Injection
In the mirror
annotation parser, the following code processes the UID from the ingress object, and inserts it into $location.Mirror.Source
in the temporary NGINX configuration. We control the ing.UID
field, which allows for a new injection point.
Since this injection is in the UID parameter, which is not a Kubernetes annotation, our input does not get sanitized by the annotations’ regex rules. Since our input gets inserted as-is, we can easily escape our context and inject arbitrary NGINX configuration directives.
CVE-2025-1974 – NGINX Configuration Code Execution
The vulnerabilities above allow an attacker to inject arbitrary directives to the NGINX configuration, which will later be tested by nginx -t
. This does not immediately lead to code execution. If we can find a directive that executes arbitrary code in nginx -t
, it will compromise the pod and obtain its highly privileged Kubernetes role. It is important to note that the NGINX configuration is only being tested, and is not being applied, reducing the number of directives we can actually (ab)use.

Figure: Partial list of available NGINX directives
Initially we tried to use the load_module
directive which can load a shared library from the filesystem. However, it can only be used in the beginning of the NGINX configuration, so when injected, load_module
will fail with the following error message:

Figure: `load_module` fails as it is specified too late in the configuration
There are many usable directives in Ingress NGINX Controller as their NGINX instance is compiled with many additional modules. We found that the ssl_engine
directive, part of the OpenSSL module, can also load shared libraries. This behavior is undocumented. Unlike load_module
, this directive can be used at any point within the configuration file, so it is suitable for our injection’s constraints.
We can now load arbitrary library files during the NGINX configuration testing phase. Our next challenge is: How can we place a shared library on the pod’s filesystem?
Uploading a shared library with NGINX Client Body Buffers
In parallel to the nginx -t
and the admission controller webhook, the pod also runs the NGINX instance itself, listening on port 80 or 443:

دیگه ترجمه رو گردن نمیگیریم :))
When processing requests, NGINX sometimes saves the request body into a temporary file (client body buffering). This happens if the HTTP request body size is greater than a certain threshold, which is by default 8KB. This means that we should theoretically be able to send a large (>8KB) HTTP request, containing our payload in the form of a shared library as the body of the request, and NGINX will temporarily save it to a file on the pod’s filesystem.
Unfortunately, NGINX also removes the file immediately, creating a nearly-impossible race condition. However, NGINX holds an open file descriptor pointing to the file, which is accessible from ProcFS:

Figure: File descriptor is still accessible from ProcFS, although the file itself is already deleted (FD #11)
To keep the file descriptor open, we can set the Content-Length
header in the request to be larger than the actual content size. NGINX will keep waiting for more data to be sent, which will cause the process to hang, leaving the file-descriptor open for longer.
The only downside to this trick is that we create the file in a different process, so we can’t use /proc/self
to access it. Instead, we will have to guess both the PID and the FD number to find the shared library, but since this is a container with minimal processes, this can be done relatively fast with a few guesses.
From Configuration Injection to RCE
With a reliable file upload to Ingress NGINX Controller’s pod, we can now put it all together to exploit this issue into a full-blown Remote Code Execution.
The exploit works as follows:
- Upload our payload in the form of a shared library to the pod by abusing the client-body buffer feature of NGINX
- Send an AdmissionReview request to the Ingress NGINX Controller’s admission controller, which contains any one of our directive injections
- The directive we inject is the
ssl_engine
directive, which will cause NGINX to load the specified file as a shared library - We specify the ProcFS path to the file descriptor of our payload
- If everything goes well, our shared library is now loaded, and we execute code remotely
Here is a demo of the exploit in practice:
سخن پایانی
ما تنها سطحی از بررسی امنیت (Admission Controllers) را کاوش کردهایم. در ابتدا، شگفتزده شدیم که چنین پایگاه کد بزرگی در پشت صحنه استفاده میشود.
به نظر ما، این سطح حمله باید به شکل بسیار بهتری محدود شود: حذف دسترسی از پادها (pods) درون کلاستر و هرگز در معرض عموم قرار نگرفتن آن.
همچنین از نبود طراحی مبتنی بر اصل حداقل دسترسی (least-privilege) شگفتزده شدیم، زیرا بهرهبرداری از این آسیبپذیری در نهایت به امتیازاتی منجر شد که امکان کنترل کامل کلاستر را فراهم کرد.
در طول این تحقیق، آسیبپذیریهای دیگری در کنترلر اینگرس NGINX پیدا کردیم و انتظار داریم در سایر Admission Controllersها نیز موارد بیشتری کشف کنیم.
در نهایت، آموختیم که دستور nginx -t
باید بهعنوان یک ابزار مضر در نظر گرفته شود. خوشحال میشویم از موارد دیگری بشنویم که در آن nginx -t
ورودیهای کاربر تصفیهنشده را در محیط واقعی پردازش میکند. این موضوع باید بهصورت واضحتری در مستندات NGINX برجسته شود.
جدول زمانی افشای مسئولانه
December 31, 2024 – Wiz Research reported CVE-2025-1974 and CVE-2025-24514 to Kubernetes.
January 2, 2025 – Wiz Research reported CVE-2025-1097 to Kubernetes. Kubernetes acknowledged the reports.
9 January 2025 – Kubernetes proposed a fix for CVE-2025-1097.
10 January 2025 – Wiz Research reported a bypass for the proposed fix for CVE-2025-1097.
12 January 2025 – Kubernetes proposed a fix for CVE-2025-1974.
16 January 2025 – Wiz Research reported a bypass for the proposed fix for CVE-2025-1974.
20 January 2025 – Kubernetes proposed a fix for CVE-2025-24513.
21 January 2025 – Wiz Research reported a bypass for the proposed fix for CVE-2025-24513.
21 January 2025 – Wiz Research reported CVE-2025-1098 to Kubernetes.
February 7, 2025 – Kubernetes released internal patches for the injection vulnerabilities: CVE-2025-1098, CVE-2025-1097, and CVE-2025-24514.
February 20, 2025 – Kubernetes notified Wiz Research that they removed the NGINX configuration validation from the admission controller, resolving CVE-2025-1974.
March 10, 2025 – Kubernetes sent embargo notifications regarding the five vulnerabilities reported by Wiz Research.
March 24, 2025 – Public disclosure.
Source: ingress-nginx-kubernetes-vulnerabilities