
NGINX Rift 漏洞分析与复现
CVE-2026-42945 (с кодовым названием "NGINX Rift") — это переполнение кучи (heap buffer overflow) в модуле ngx_http_rewrite_module NGINX. Оценка CVSS v4: 9.2 (Critical).
Уязвимость была обнаружена исследовательской группой depthfirst в апреле 2026 года и скрывалась в NGINX 18 лет, начиная с версии 0.6.27 (введена в 2008 году).
Для возникновения уязвимости необходима следующая конфигурация NGINX:
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
Ключевые условия:
rewrite содержит ? (вопросительный знак)set ссылается на группу захвата регулярного выражения (например, $1)+, &, % и т.д.)Скриптовый движок NGINX использует двухфазную обработку для выполнения директив rewrite/set:
Ключевая проблема — несоответствие состояния движка между двумя фазами:
rewrite устанавливает флаг is_argsКогда строка замены директивы rewrite содержит ?, функция ngx_http_script_start_args_code устанавливает:
void ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
{
e->is_args = 1; // Устанавливается навсегда, никогда не сбрасывается!
e->args = e->pos;
e->ip += sizeof(uintptr_t);
}
set использует новый поддвижокКогда последующая директива set ссылается на группу захвата, ngx_http_script_complex_value_code создаёт обнулённый поддвижок:
void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)
{
ngx_http_script_engine_t le;
// ...
ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); // le.is_args = 0
le.ip = code->lengths->elts;
Вычисление длины (использует поддвижок le, is_args=0):
// ngx_http_script_copy_capture_len_code
if ((e->is_args || e->quote) && (e->request->quoted_uri || e->request->plus_in_uri))
{
// is_args=0, условие ложно, выполняется ветка else
return cap[n + 1] - cap[n]; // Возвращается исходная длина (неэкранированная)
}
Реальное копирование (использует основной движок e, is_args=1):
// ngx_http_script_copy_capture_code
if ((e->is_args || e->quote) && (e->request->quoted_uri || e->request->plus_in_uri))
{
// is_args=1, условие истинно, выполняется ветка if
e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
cap[n + 1] - cap[n],
NGX_ESCAPE_ARGS);
// Каждый экранируемый символ расширяется с 1 байта до 3 байт!
}
raw_size (исходная длина захвата)raw_size + 2 * N (N = количество экранируемых символов)Например, если URI содержит 100 символов +, переполнение составит 200 байт.
Самый простой способ — отправить запрос с большим количеством экранируемых символов, что приводит к аварийному завершению рабочего процесса:
GET /api/+++++++++++++++++++++++++++++++++++ HTTP/1.1
Host: target.com
Полная цепочка RCE (требуется отключённый или обойдённый ASLR):
ngx_pool_t через порядок соединенийcleanup: переполнение до соседней структуры пула памятиsystem()ngx_destroy_pool, обходящий цепочку cleanupМногопроцессная архитектура NGINX делает эксплуатацию более надёжной — после аварийного завершения рабочего процесса мастер порождает новый рабочий процесс с идентичной компоновкой памяти.
README.md — данный файл, документ с анализом уязвимостиDockerfile — создание уязвимого окружения NGINXnginx.conf — конфигурация NGINX для активации уязвимостиpoc_crash.py — PoC DoS (вызов аварийного завершения рабочего процесса)docker-compose.yml — запуск тестового окружения одной командой# 1. Сборка и запуск уязвимого NGINX
docker-compose up -d
# 2. Запуск DoS PoC
python3 poc_crash.py
# 3. Просмотр журнала ошибок NGINX для подтверждения аварийного завершения
docker-compose logs nginx
Данный материал предназначен только для исследовательских и образовательных целей. Не используйте эту информацию для несанкционированных атак.