NGINX Rift — CVE-2026-42945 | TryHackMe Walkthrough
Estudo prático de segurança: Heap Buffer Overflow, corrupção de memória e RCE no NGINX.
Ambiente: laboratório TryHackMe com NGINX vulnerável em Docker. Os comandos de exploração apresentados aqui são destinados exclusivamente ao ambiente autorizado.
1. Visão geral
O NGINX Rift é uma vulnerabilidade de heap buffer overflow no módulo ngx_http_rewrite_module. Em determinadas configurações, uma requisição HTTP pode fazer o NGINX reservar menos memória do que realmente precisa, sobrescrevendo dados adjacentes na heap.
Isso pode causar DoS (queda dos workers) ou, quando a corrupção de memória é explorada de forma mais elaborada, RCE (execução remota de comandos). O exploit publicado demonstra RCE em um laboratório preparado com ASLR desabilitado; isso não significa que qualquer servidor com NGINX vulnerável possa ser explorado da mesma forma.
| Item | Informação |
|---|---|
| CVE | CVE-2026-42945 |
| Componente | ngx_http_rewrite_module |
| Tipo | Heap Buffer Overflow |
| Possível impacto | DoS e RCE, dependendo das condições |
| NGINX Open Source corrigido | 1.30.1 / 1.31.0 |
| Pré-condição importante | Combinação específica de rewrite, captura numérica ($1, $2 etc.), ? e diretivas seguintes |
2. Antes do exploit: para que serve rewrite?
O rewrite altera internamente o caminho de uma requisição. Exemplo de uma loja que reorganizou seus produtos:
rewrite ^/old/(.*)$ /new/$1 last;
| Parte | Significado |
|---|---|
^/old/ |
O caminho começa com /old/ |
(.*) |
Captura o restante do caminho |
$1 |
Reutiliza o conteúdo do primeiro grupo capturado |
/new/$1 |
Novo caminho interno |
last |
Encerra o conjunto atual de regras e procura uma nova location |
Se o cliente pedir /old/notebook-lg, o NGINX poderá processar internamente /new/notebook-lg. Ele não move arquivos e, nesse caso, o endereço mostrado no navegador não precisa mudar. Isso é útil para preservar compatibilidade com caminhos antigos. Para avisar o navegador e mecanismos de busca de uma mudança permanente de URL, pode-se usar um redirecionamento HTTP 301:
rewrite ^/old/(.*)$ /new/$1 permanent;
Mesmo que a equipe atualize os links do próprio site, URLs antigas podem continuar em favoritos, e-mails, links externos e mecanismos de busca.
O set, por sua vez, armazena valores em variáveis que serão usadas pela configuração:
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /v2/api/$1;
set $original_endpoint $1;
}
Na lógica desse exemplo, $1 representa o trecho capturado, como users/42, e $original_endpoint serve para guardar esse trecho.
Atenção ao ?: na substituição de um rewrite, ele marca o começo dos novos parâmetros (query string). Por exemplo, /internal?migrated=true define o caminho /internal e o parâmetro migrated=true.
3. Onde está a falha?
O motor de scripts do NGINX trabalha em duas passagens:
- Length: calcula quantos bytes serão necessários e reserva memória.
- Copy: escreve efetivamente os dados no buffer reservado.
A configuração que aciona a falha no laboratório é:
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
O ? no rewrite ativa a variável interna is_args = 1 no motor principal. Quando o set utiliza uma captura numérica ($1), o cálculo de tamanho acontece em outro motor, recém-inicializado, no qual is_args = 0. Já a etapa de cópia usa o motor principal, em que is_args continuou ativado.
| Passagem | Estado de is_args |
Resultado |
|---|---|---|
| Cálculo do tamanho | 0 |
Considera o comprimento bruto da captura |
| Escrita dos dados | 1 |
Faz o escape de caracteres especiais da captura |
Por exemplo, + ocupa 1 byte na captura, mas pode ser escrito como %2B (3 bytes) na etapa de escape. Com 2.000 sinais de +:
Length: reserva 2.000 bytes
Copy: tenta escrever 6.000 bytes
└── 4.000 bytes além do espaço reservado
Os bytes excedentes invadem regiões próximas da heap, podendo sobrescrever outros dados. O buffer não aumenta automaticamente.
Uma requisição legítima também pode provocar a falha?
Sim, se cumprir a combinação necessária. Compare:
GET /api/produtos?nome=notebook+lg HTTP/1.1
Nesse caso, o + está na query string, fora do trecho capturado por (.*) no caminho /api/produtos.
GET /api/notebook+lg HTTP/1.1
Agora + está dentro da captura $1, podendo participar da expansão problemática. Uma requisição comum pode acionar a falha acidentalmente; um atacante usa o comportamento deliberadamente, geralmente ampliando o volume de caracteres escapáveis.
4. Como o overflow pode virar RCE?
Sobrescrever memória não equivale, por si só, a executar código. O exploit precisa alterar alguma estrutura que o NGINX utilizará depois.
O NGINX administra memória por meio de estruturas ngx_pool_t. Elas possuem uma lista de rotinas de limpeza (cleanup). Cada entrada inclui:
| Campo | Para que serve |
|---|---|
handler |
Ponteiro para a função de limpeza |
data |
Argumento passado à função |
Quando o pool é destruído, o NGINX executa conceitualmente handler(data). A técnica publicada busca corromper o ponteiro da lista de cleanup de um pool adjacente, fazendo-o apontar para uma estrutura falsa preparada em memória.
Heap feng shui é a organização cuidadosa das alocações na heap. No laboratório, duas conexões HTTP são usadas para tentar posicionar pools próximos: uma conexão fica com cabeçalhos incompletos, outra cria um pool vizinho, a primeira é completada e provoca o overflow, e a segunda é encerrada para acionar a limpeza do pool corrompido.
Heap spraying significa colocar muitas cópias de estruturas preparadas na memória para aumentar a chance de um ponteiro adulterado alcançar uma delas. A PoC utiliza corpos de requisições POST para posicionar estruturas binárias, pois os bytes que passam pelo escape da URL têm restrições.
ASLR: a PoC original demonstra RCE com a randomização de endereços desabilitada no laboratório. Com ASLR habilitado, a exploração para RCE exige superar proteções adicionais. O DoS por corrupção de memória continua sendo um risco para configurações vulneráveis.
5. Reprodução no laboratório TryHackMe
O ambiente fornecido já contém um container vulnerável. Os exemplos abaixo pressupõem que você está no terminal da VM/VNC do laboratório, no diretório onde se encontra poc.py.
Passo 1 — Confirmar o container
sudo docker ps --filter label=com.docker.compose.project=nginx-rift
docker ps: mostra containers ativos.--filter label=...: limita a listagem ao projeto.
Na sala, o container esperado chama-se nginx-rift-nginx-1; a porta 19321 do host está mapeada para o NGINX.
Teste benigno:
curl -s 'http://127.0.0.1:19321/api/users/42'
curl realiza a requisição HTTP e -s evita exibir o indicador de progresso.
Passo 2 — Testar execução de um único comando
time python3 poc.py --cmd 'echo hello from depthfirst > /tmp/pwned'
| Elemento | Função |
|---|---|
time |
Mede o tempo do processo |
python3 poc.py |
Executa a PoC disponibilizada no laboratório |
--cmd |
Seleciona o modo de comando único |
echo ... |
Produz uma mensagem simples de confirmação |
> |
Redireciona a saída para o arquivo |
/tmp/pwned |
Arquivo de prova criado dentro do container |
A PoC tenta obter a execução por meio da corrupção de memória e da rotina de cleanup. Para verificar o resultado no container:
sudo docker exec nginx-rift-nginx-1 cat /tmp/pwned
Saída esperada:
hello from depthfirst
Isso comprova que o comando foi executado dentro do container, e não apenas no terminal do host.
Passo 3 — Reverse shell no laboratório
python3 poc.py --shell
Nesse modo, a PoC inicia um listener e prepara uma conexão reversa do container para o host. No ambiente fornecido, utiliza a ponte Docker 172.17.0.1 e a porta TCP 1337.
Após receber a shell:
id
cat /flag.txt
id: identifica o usuário efetivo da sessão.cat /flag.txt: lê a flag da room.
Observação: o laboratório executa o handler com privilégios root dentro do container. Isso não demonstra, por si só, acesso root ao sistema host nem um escape de container.
Fluxo da exploração
Requisição HTTP preparada
↓
Diferença entre cálculo e cópia (is_args)
↓
Heap Buffer Overflow
↓
Corrupção de estrutura de cleanup
↓
Destruição do pool e chamada do handler adulterado
↓
Execução de comando / reverse shell no laboratório
6. Dicas de estudo e validação
- Entenda primeiro o funcionamento de
rewrite,(.*),$1,sete?. O erro está no estado interno entre as duas passagens, não na existência dessas diretivas isoladamente. - Diferencie path (
/api/notebook+lg) de query string (?nome=notebook+lg). O local da captura faz diferença. - Um
200 OKem uma requisição normal confirma disponibilidade, não confirma sozinho a presença ou a ausência da CVE. - Se o worker cair, isso demonstra instabilidade/corrupção possível, não comprova RCE. O arquivo
/tmp/pwnede o resultado deidsão verificações independentes no laboratório. - Ao terminar, compare a configuração vulnerável com a mitigada e confira a versão e os avisos de segurança do pacote realmente instalado.
7. Mitigação e correção
Atualizar o NGINX
A correção principal é usar uma versão que incorpore o patch, incluindo NGINX Open Source 1.30.1 ou 1.31.0 e os patches correspondentes do NGINX Plus. Produtos F5 que incorporam NGINX possuem versões corrigidas próprias; consulte o aviso do fornecedor.
No Ubuntu/Debian:
nginx -v
apt-cache policy nginx
No RHEL e derivados:
nginx -v
dnf info installed nginx
Nota: pacotes de distribuições podem receber backports de segurança sem mudar o número principal de versão. Verifique também o changelog e o advisory da distribuição.
Workaround: capturas nomeadas
Enquanto não for possível atualizar, a mitigação descrita no material da room é evitar capturas numéricas no padrão afetado:
Antes:
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
Depois:
location ~ ^/api/(?<path>.*)$ {
rewrite ^/api/(?<path>.*)$ /internal?migrated=true;
set $original_endpoint $path;
}
(?<path>.*) nomeia a captura e $path recupera seu conteúdo. Essa mudança evita o caminho de avaliação de captura numérica envolvido na falha. É mitigação temporária, não substitui o patch. Confira o comportamento da aplicação depois da alteração.
Teste e recarregue a configuração no host, caso o NGINX seja gerenciado pelo systemd:
sudo nginx -t && sudo systemctl reload nginx
Monitorar logs
Procure padrões incomuns de sinais de + nas URLs:
grep -E '\+{30,}' /var/log/nginx/access.log
Correlacione com erros de workers:
grep 'exited on signal 11' /var/log/nginx/error.log
O signal 11 costuma indicar uma falha de acesso à memória (segmentation fault), mas não é prova isolada de exploração dessa CVE. Requisições longas e encerramentos recorrentes de workers no mesmo intervalo merecem investigação. Um WAF pode ajudar a filtrar padrões anômalos na rota afetada, observando falsos positivos.
8. Conclusão
O principal aprendizado é que duas etapas aparentemente simples — calcular o tamanho e copiar os dados — precisam concordar exatamente. No Rift, o rewrite deixa is_args ativado, enquanto o cálculo de um set com captura numérica usa um motor zerado. A cópia pode expandir caracteres sem que o buffer tenha espaço suficiente. O resultado é corrupção da heap, com risco de DoS e, sob condições específicas demonstradas pela PoC, RCE.
Referências
- PoC e código de referência — depthfirst / Nginx-Rift
- Pesquisa técnica — NGINX Rift
- Advisory oficial F5 — K000160932
- Documentação NGINX — rewrite module
Walkthrough educacional baseado em reprodução autorizada no TryHackMe. Os resultados de RCE/root descritos pertencem ao container preparado para a room.