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:

  1. Length: calcula quantos bytes serão necessários e reserva memória.
  2. 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, set e ?. 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 OK em 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/pwned e o resultado de id sã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


Walkthrough educacional baseado em reprodução autorizada no TryHackMe. Os resultados de RCE/root descritos pertencem ao container preparado para a room.