NGINX Rift: 18-летний баг в rewrite-модуле и почему твой сервер под угрозой прямо сейчас.
Представь: твой nginx работает уже несколько лет. HTTPS настроен, заголовки безопасности выставлены, fail2ban банит ботов. Ты закрыл все очевидные дыры. И тут кто-то присылает один-единственный HTTP-запрос — такой же как тысячи других — и твой воркер-процесс падает. Если ASLR у тебя выключен — они уже выполняют произвольный код от имени nginx.
Ничего не сломано снаружи. Nginx поднял новый воркер и продолжает работать. Никакого алерта. Просто в логах мелькнула строчка «worker process exited on signal 11».
Добро пожаловать в CVE-2026-42945, известный как NGINX Rift.
ОН БЫЛ ВЕЗДЕ — И НИКТО НЕ ЗАМЕЧАЛ
13 мая 2026 года F5 совместно с исследовательской платформой depthfirst раскрыли критическую уязвимость в ngx_http_rewrite_module. Это модуль, который отвечает за обработку директив rewrite, if и set — то есть за перезапись URL. Почти любой нетривиальный конфиг nginx его использует.
Уязвимость существует с 2008 года. С версии nginx 0.6.27. Это значит, что она сидела в исходниках через все версии, через все security-аудиты, через все обновления. 18 лет.
Затронуто: NGINX Open Source 0.6.27 — 1.30.0, NGINX Plus R32 — R36, и всё что собрано на том же ngx_http_script.c — включая Ingress NGINX в Kubernetes-кластерах.
CVSS v4.0: 9.2 Critical. CVSS v3.1: 8.1 High.
Интересная деталь про то как её нашли: depthfirst загрузил исходники nginx в свою AI-систему анализа кода и получил результат примерно за шесть часов. Шесть часов против восемнадцати лет. Это не упрёк в адрес nginx — это сигнал о том, что ручной code review больше не справляется с таким классом уязвимостей.
ЧТО ТАКОЕ HEAP BUFFER OVERFLOW И ПОЧЕМУ ЭТО ПЛОХО
Прежде чем разбирать конкретный баг — секунда на контекст, потому что этот термин важен.
Когда nginx обрабатывает запрос, он выделяет кусок памяти (heap) для работы с данными. Buffer overflow — это когда программа записывает данных больше, чем помещается в выделенный кусок, и начинает затирать соседнюю память. В этой соседней памяти могут находиться указатели на функции, управляющие структуры, что угодно.
Почему это критично: если атакующий может контролировать что именно записывается за границу буфера, он может перезаписать указатель на функцию своим адресом. Когда nginx вызовет эту функцию — выполнится код атакующего. Это и есть Remote Code Execution.
КАК РАБОТАЕТ БАГ
nginx обрабатывает rewrite-правила в два прохода по коду.
Первый проход — разведчик. Он проходит по правилу и считает: сколько байт нужно выделить под результирующую строку? Смотрит на паттерн, на replacement, на captures — формирует итоговый размер буфера.
Второй проход — исполнитель. Получает уже выделенный буфер нужного размера и копирует туда данные.
Проблема в одном флаге под названием is_args. Он выставляется на первом проходе, когда в строке замены встречается знак вопроса — это сигнал что дальше идёт query string, то есть параметры вида ?id=123. Логика правильная: флаг нужен чтобы первый проход корректно посчитал длину.
Но флаг не сбрасывается между проходами. Он просачивается во второй — и там заставляет функцию ngx_escape_uri работать в другом режиме, с другой логикой экранирования, с другим расчётом длины. В итоге второй проход пишет больше байт чем выделил первый. Буфер переполняется.
Срабатывает только при двух одновременных условиях:
— unnamed PCRE captures в правиле: $1, $2, $3 (это когда группа в regex без имени — просто круглые скобки)
— знак вопроса в строке замены
Пример — типичный API-прокси:
location /api/ {
rewrite ^/api/(.*)$ /internal?id=$1 last;
}
Тут есть и unnamed capture (.*) — это $1 — и вопросительный знак перед id=$1. Оба условия выполнены. Этот конфиг уязвим.
И это не редкость. Это стандартный паттерн который встречается в половине туториалов по nginx. Авторы писали его корректно — они просто не знали, что внутри таится флаг который неправильно сбрасывается.
КАК ИМЕННО ЭКСПЛУАТИРУЕТСЯ
Атакующий отправляет HTTP-запрос на уязвимый endpoint. В URI — длинная строка из повторяющихся символов, например сотни знаков +.
Первый проход считает длину буфера исходя из нормальной логики — без учёта просочившегося флага. Буфер выделяется.
Второй проход начинает копировать — и из-за неправильного флага ngx_escape_uri начинает добавлять escape-последовательности (%2B вместо +, например). Строка растёт. Выходит за границу буфера. Затирает соседнюю память.
Критически важная деталь: данные которые пишутся за границу буфера — это URI из запроса атакующего. Он контролирует что именно туда попадает. Это не случайное затирание памяти мусором — это управляемая запись.
ЧТО ПРОИСХОДИТ ДАЛЬШЕ — ЗАВИСИТ ОТ НАСТРОЕК СИСТЕМЫ
С включённым ASLR (Address Space Layout Randomization, параметр kernel.randomize_va_space = 2):
При каждом запуске процесса адреса в памяти рандомизируются. Атакующий не знает где именно в памяти находится то что он хочет перезаписать. Переполнение происходит, но попадает в случайное место — nginx падает с signal 11 (segfault) и перезапускается. Это Denial of Service: неприятно, сайт моргает, но не катастрофа.
Без ASLR или с ASLR выключенным:
Адреса предсказуемы. Атакующий знает куда писать. Результат — детерминированный RCE: один запрос, произвольный код от имени nginx.
Но есть нюанс с ASLR: depthfirst задокументировал в своём PoC технику под названием progressive heap shaping. Это серия специально подобранных запросов, которые постепенно «укладывают» heap в нужную форму — так чтобы нужные структуры оказались в предсказуемых местах даже с ASLR. Это сложнее одного запроса, требует времени, но работает. То есть ASLR не абсолютная защита — он поднимает планку, но не закрывает дыру.
16 мая — через три дня после публикации PoC — VulnCheck зафиксировал первые реальные попытки эксплуатации в интернете.
ЦЕПОЧКА АТАКИ В РЕАЛЬНОМ МИРЕ
NGINX Rift особенно опасен не сам по себе, а в связке с другими майскими уязвимостями.
NGINX Rift даёт атакующему выполнение кода в контексте nginx-воркера — пользователь www-data или nginx, непривилегированный. Само по себе неприятно, но не root.
Dirty Frag (CVE-2026-43284 / CVE-2026-43500) — уязвимость ядра Linux, раскрытая в начале мая 2026. Позволяет любому непривилегированному пользователю получить root через переполнение page cache в модулях esp4/esp6/rxrpc.
Цепочка: один HTTP-запрос → foothold в nginx-воркер → Dirty Frag → root на сервере. Без аутентификации. Без учётных записей. Без следов в логах приложения.
Это не теоретическая атака. Патч для обоих нужен прямо сейчас.
ПРОВЕРИТЬ СЕБЯ — ТРИ КОМАНДЫ
Первое — версия nginx. Нужно 1.30.1 или выше:
nginx -v
Если видишь что-то вроде nginx/1.24.0 или nginx/1.18.0 — уязвим.
Второе — есть ли уязвимые правила в конфиге. Ищем rewrite-директивы с $1/$2 и вопросительным знаком:
grep -rn "rewrite" /etc/nginx/ | grep -E '\$[0-9].*\?'
Пустой вывод — хорошо. Непустой — у тебя уязвимая конфигурация, обновляйся немедленно независимо от версии nginx (будущий конфиг на новой версии будет безопасен, но проверить стоит).
Третье — ASLR. Пока не обновился, убедись что включён:
cat /proc/sys/kernel/randomize_va_space
Должно быть 2. Если 0 или 1 — это значит что RCE одним запросом. Включить немедленно:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
echo "kernel.randomize_va_space = 2" | sudo tee /etc/sysctl.d/99-aslr.conf
sudo sysctl -p /etc/sysctl.d/99-aslr.conf
Это не закрывает уязвимость — но переводит сценарий с RCE на DoS. Значительно лучше.
ОБНОВЛЕНИЕ
Обновление — единственное полное решение. Конфигурационных воркараундов недостаточно для production.
Debian/Ubuntu:
sudo apt update && sudo apt install nginx
nginx -v
Важный момент: официальные репозитории Ubuntu и Debian иногда отстают на несколько дней от upstream. Если apt выдаёт версию ниже 1.30.1 — подключи официальный репозиторий nginx.org напрямую. Это безопасно: ключи GPG верифицированы.
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update && sudo apt install nginx
RHEL/AlmaLinux/Rocky Linux:
sudo dnf update nginx
nginx -v
После обновления — обязательно проверить конфиг и перезагрузить. Иногда после мажорного обновления nginx меняет поведение директив и конфиг перестаёт валидироваться:
sudo nginx -t && sudo systemctl reload nginx
nginx -t проверяет синтаксис конфига. Если ошибок нет — reload применяет новую версию без даунтайма. Если есть — разбираешь что сломалось перед применением.
ЕСЛИ ОБНОВИТЬСЯ ПРЯМО СЕЙЧАС НЕВОЗМОЖНО
Иногда production нельзя трогать прямо сейчас: релиз, freeze, согласования. На этот случай — конфигурационная митигация.
Уязвимость требует двух условий одновременно: unnamed capture ($1, $2) и вопросительный знак в replacement. Убери одно — баг не сработает.
Способ первый — заменить unnamed captures на named. Named captures работают иначе на уровне кода и не задействуют уязвимый путь:
# Было — уязвимо (unnamed capture, знак вопроса):
rewrite ^/api/(.*)$ /internal?id=$1 last;
# Стало — безопасно (named capture):
rewrite ^/api/(?P.*)$ /internal?id=${id} last;
(?P.*) — это named capture с именем id. ${id} — обращение к нему по имени. Функционально то же самое, но другой путь в коде.
Способ второй — убрать rewrite с вопросительным знаком, переписать через proxy_pass:
# Вместо rewrite:
location ~ ^/api/(.+)$ {
proxy_pass http://backend/internal?id=$1;
}
proxy_pass не проходит через ngx_http_rewrite_module — уязвимость не применима.
Это временная мера. Обновление всё равно нужно.
KUBERNETES И INGRESS NGINX — ОСОБЫЙ СЛУЧАЙ
Если используешь Ingress NGINX Controller в Kubernetes — ситуация сложнее.
В марте 2026 года Kubernetes-проект вывел Ingress NGINX из поддержки. Последняя версия — v1.15.1. Она содержит тот же ngx_http_script.c с той же уязвимостью. И upstream-патча не будет — проект закрыт.
Особенность ingress controller: он обычно доступен с публичного интернета прямо на edge кластера. NGINX Rift здесь — уязвимость без аутентификации, достижимая напрямую из интернета, без патча.
Миграция: nginx-gateway-fabric (официальный наследник от F5), Traefik, или Envoy. Это не быстрая задача, но она уже не опциональная.
ПОЧЕМУ ЭТО НЕ «ЕЩЁ ОДИН CVE»
nginx работает примерно на 34% всех веб-серверов мира. Он стоит перед backend, перед базами данных, перед API — первым получает каждый входящий запрос, до любой другой защиты. Cloudflare сверху? Правила WAF? Отлично. Но запросы от легитимных пользователей всё равно доходят до nginx — и один из них может быть crafted.
Разница между этим CVE и большинством других в том, что:
— баг в коде который выполняется при обработке каждого rewrite-запроса
— не требует аутентификации
— не требует особых прав
— работает через обычный HTTP
— PoC публичный с первого дня
Обновление nginx — это не «поставлю на выходных». Эксплуатация уже идёт.
