Repositório completo de pesquisa sobre CVE-2026-42945 com análise de estouro de buffer heap, exploit de RCE (heap spray + Feng Shui), scripts de detecção e orientações de correção para a vulnerabilidade do módulo de reescrita do NGINX.
Estouro de Buffer na Heap no ngx_http_rewrite_module do NGINX
| Métrica | Valor |
|---|---|
| CVSS v4.0 | 9.2 (Crítico) |
| CVSS v3.1 | 8.1 (Alto) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | 122 — Estouro de Buffer Baseado em Heap |
| Introduzido | Junho de 2008 — v0.6.27 |
| Descoberto | Abril de 2026 — DepthFirst Research |
| Corrigido | 13 de Maio de 2026 — v1.30.1, v1.31.0 |
| CVE Publicado | 21 de Maio de 2026 |
| Tempo de Vida | ~18 anos (não detectado) |
| Commit da Correção | 524977e7c534e87e5b55739fa74601c9f1102686 |
Um atacante remoto não autenticado pode desencadear um estouro de buffer determinístico na heap nos processos worker do NGINX enviando uma requisição HTTP maliciosa para um servidor com um padrão de configuração específico de rewrite + set/if/rewrite. O estouro corrompe metadados da heap (ponteiros ngx_pool_cleanup_t), possibilitando a Execução Remota de Código (RCE) através de técnicas de heap spray e Feng Shui.
server { listen 19321;
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
}
**Key requirements:**
- Uma diretiva `rewrite` cuja substituição contenha `?` (separador de string de consulta)
- Uma diretiva `set`, `if` ou `rewrite` subsequente que referencie uma **captura PCRE sem nome** (`$1`, `$2`, etc.)
- O `?` na substituição do rewrite aciona `ngx_http_script_start_args_code` que define `e->is_args = 1`
### O que um atacante pode alcançar
| Capacidade | Descrição |
|-----------|-------------|
| **Negação de Serviço** | Quebrar processos workers de forma determinística, causando loops de reinicialização (funciona independentemente de ASLR) |
| **Execução Remota de Código** | Com ASLR desativado (ou contornado via sobrescrita parcial), obter RCE total como o usuário do nginx |
| **Exfiltração de Dados** | Através de primitivas de leitura de memória, extrair dados sensíveis do heap do worker |
| **Persistência** | Instalar backdoors via execução de código na memória do processo worker |
---
## 2. Análise da Causa Raiz
### O Mecanismo de Script de Duas Passagens
O `ngx_http_rewrite_module` do NGINX usa um **mecanismo de script de duas passagens** em `src/http/ngx_http_script.c`:
1. **Passagem de Comprimento** (`ngx_http_script_run`): itera todos os códigos de script para calcular o tamanho total do buffer necessário. Escreve comprimentos em `le.ip` e `le.pos`.
2. **Passagem de Cópia** (`ngx_http_script_copy_len`/`_code`): itera novamente, escrevendo bytes reais no buffer pré-alocado em `e->ip` e `e->pos`.
Cada código de script possui dois manipuladores: um para cada passagem. Por exemplo:
- `ngx_http_script_copy_len` → `ngx_http_script_copy_code`
- `ngx_http_script_start_args_len` → `ngx_http_script_start_args_code`
### A Flag `is_args`
A flag `e->is_args` na **estrutura do motor** (`ngx_http_script_engine_t`) controla como a passagem de cópia lida com certos caracteres:```c
typedef struct {
u_char *ip;
u_char *pos;
ngx_http_variable_value_t *sp;
ngx_str_t buf;
int flushed;
unsigned is_args:1; // <-- THE BUG
unsigned ncaptures:1;
ngx_uint_t captures_size;
// ...
} ngx_http_script_engine_t;
Quando e->is_args = 1, o código de cópia para referências de captura $N chama ngx_escape_uri() com NGX_ESCAPE_ARGS, que expande:
+ → %2B (1 byte → 3 bytes, +200%)% → %25 (1 byte → 3 bytes, +200%)& → %26 (1 byte → 3 bytes, +200%)O fluxo de execução para o padrão vulnerável:``` rewrite ^/api/(.*)$ /internal?migrated=true;
1. Durante a **avaliação da reescrita**, o mecanismo encontra `?` na string de substituição, o que dispara `ngx_http_script_start_args_code`, definindo `e->is_args = 1`.
2. A reescrita modifica o URI da requisição e então continua para a próxima diretiva.
3. **`e->is_args` NUNCA É LIMPO**.
Então:```
set $original_endpoint $1;
le) é criado para a passagem de comprimento: ```c
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
Isso zera corretamente le.is_args = 0, então a passagem de comprimento retorna o comprimento da captura bruto, sem escape.
e, que ainda possui e->is_args = 1 desde o passo 1. A passagem de cópia aplica escape de URI, expandindo cada caractere escapável de 1 byte para 3 bytes dentro de um buffer que foi dimensionado para o comprimento bruto — estouro de heap.Pass 1 (Length — sub-engine le): le.is_args = 0 capture $1 = "A+++++B" → length = 7
Buffer allocated: 7 bytes
Pass 2 (Copy — main engine e): e.is_args = 1 ← LEAKED from rewrite capture $1 = "A+++++B" ngx_escape_uri("A+++++B", NGX_ESCAPE_ARGS): A → A (1 byte) + → %2B (3 bytes) ← EXPANSION + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) + → %2B (3 bytes) B → B (1 byte) total written: 17 bytes buffer size: 7 bytes OVERFLOW: 10 bytes
A taxa de expansão é `7 + (n_escapable * 2)` onde `n_escapable` é a contagem de `+`, `%` e `&` na captura.
---
## 3. Mecânicas de Exploração
### Visão Geral
| Passo | Técnica | Descrição |
|------|-----------|-------------|
| 1 | Overflow | Enviar URI maliciosa com preenchimento de `+` para estourar o buffer da heap |
| 2 | Heap Spray | Fazer POST de corpos grandes para `/spray` para preencher a heap com dados controlados |
| 3 | Feng Shui | Organizar alocações para que o alvo do estouro (`ngx_pool_cleanup_t`) fique adjacente |
| 4 | Corrupt Handler | O estouro sobrescreve `ngx_pool_cleanup_t.handler` com o endereço de `system()` |
| 5 | Trigger Cleanup | Aguardar a destruição do pool → `system(cmd)` executa o comando do atacante |
| 6 | Reverse Shell | Encadear com payload de reverse shell para acesso interativo |
### Feng Shui Entre Requisições
**Feng Shui de requisição única falha** porque o estouro corrompe os metadados do pool (`->d.next`, `->d.failed`) antes de alcançar o ponteiro `cleanup`. Quando o pool é destruído ao final da requisição, os metadados corrompidos causam uma **quebra antes que `system()` seja chamada**.