
Apache Tomcat 11.0.27 - Alto - Remoto - DoS - RewriteValve [NS] worker pin
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · tomcat-rewrite-ns-loop
Classe: DoS Alcance: Remoto
Apache Tomcat 11.0.27 (52f3b364) - tomcat.apache.org
RewriteValve.parseRuleFlag faz correspondência por prefixo. Após NC/NE, qualquer flag restante que startsWith("N") habilita a próxima. O [NS] (nosubreq) e [NV] (novary) copiados do httpd tornam-se [N]. A substituição - deixa substitution nulo; RewriteRule.evaluate então retorna a URL original. isNext() mais um newtest não nulo define i = -1 sem limite de rodadas, então um único GET não autenticado que corresponda à regra prende a thread do conector HTTP até o cliente desconectar ou o processo reiniciar. [N] explícito e [S=-1] são o mesmo travamento. [L,NS] é seguro porque isLast() interrompe primeiro.
CVE-2026-65927 (Important, 11.0.25) corrigiu [N] reiniciando na segunda regra. Este travamento ainda não tem limite de rodadas. O main do mesmo dia após 11.0.27 adicionou MAX_REWRITE_ROUNDS=1000 e correspondência exata de flags (2da9fddb, 8992dbdd) sem um aviso de segurança.
| ID | ainda sem CVE |
| Classe | DoS |
| Alcance | Remoto (HTTP não autenticado) |
| CWE | CWE-835 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| Produto | Apache Tomcat |
| Afetado | 11.0.27 (52f3b364e5882a5dbcfc42cb8313a7c2ae532c64) RewriteValve |
| Autenticação | HTTP não autenticado |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Fiz o pin da tag 11.0.27 e li RewriteValve.invoke. A valve não está no conf/server.xml padrão. O caminho padrão do Host é conf/Catalina/localhost/rewrite.config. O lab é HTTP em loopback em 127.0.0.1:18910. Tarball oficial 11.0.27. Não é RCE.
| Método | Sink | Privilégio |
|---|---|---|
GET /loop não autenticado correspondendo ao Host rewrite.config RewriteRule ^/loop$ - [NS] | RewriteValve.invoke i = -1 porque startsWith("N") no token NS | Disponibilidade (comprovada em lab): prende uma thread do conector HTTP até o cliente desconectar ou o processo reiniciar. Lab: /loop expirou em 4,002s. |
GET /next não autenticado com [N] explícito | mesmo reinício de isNext() | Disponibilidade (comprovada em lab): mesmo travamento. Lab: /next expirou em 4,003s. |
GET /skip não autenticado com [S=-1] | i += -1 e então o incremento do for | Disponibilidade (comprovada em lab): mesmo travamento. Lab: /skip expirou em 4,002s. |
| Uma requisição correspondente por worker | mesmo sink, maxThreads URLs correspondentes concorrentes | Disponibilidade (inferida): esgota o pool do conector (o lab usou 32; o padrão de fábrica é 200). Novas requisições então entram na fila ou são recusadas. O /ok irmão ainda respondeu enquanto um /loop estava em andamento. |
Travamento vs 404 de 13ms em /ok | timing de next/skip vs [L] | Recon (comprovada em lab): detecta que uma regra next/skip correspondeu. Lab: travamento de 4,002s vs 404 de 0,013s. |
Comprovado em lab vs inferido: o travamento de /loop [NS], o travamento de /next [N], o travamento de /skip [S=-1], o 404 de /ok [L] em 13ms, o 404 de /safe [L,NS] em 5ms, e o 404 do /ok irmão enquanto /loop está em andamento são comprovados em lab. O esgotamento do pool é inferido a partir do travamento de uma thread mais maxThreads.
Requer: RewriteValve no Host ou Context e uma regra correspondente em rewrite.config. A valve é opt-in, mesma classe do AJP comentado. O [NS] copiado do httpd é uma regra correspondente, não um loop [N] infinito intencional.
| Superfície | Lab |
|---|---|
[L,NS] (last+nosubreq do httpd) | /safe 404 em 5ms (L-ns-safe=yes) |
| Outras requisições em andamento enquanto uma thread está presa | /ok 404 em 13ms durante /loop (sibling-alive=yes) |
server.xml padrão sem RewriteValve | valve ausente; o leftover não dispara |
| RCE / leitura de WEB-INF / bypass de restrição do CVE-2026-65927 | [N] agora reinicia na regra 0; este leftover é apenas disponibilidade |
| Manager / JMX / AJP / PUT do DefaultServlet | inalterado; PUT ainda readonly |
Sem execução de código como o usuário do Tomcat. Sem leitura de conf/ ou WEB-INF. Sem roubo de sessão. Sem deploy pelo Manager. A JVM permanece ativa; outros workers ainda respondem até o pool ser preenchido.
cd lab
./run.sh
Loopback 127.0.0.1:18910 mapeia o container 8080. Projeto Compose tomcat-rewrite-ns-loop. Tag da imagem tomcat-rewrite-ns-loop:11.0.27. apache-tomcat-11.0.27.tar.gz oficial SHA512 a617896307cada4884b24f80c4e977ddad8b3e0f88ce0e2a6eea29d5f332498e276ae5976c227a9c8daecf05c7c90def684a3766dd5a81f47a56d8b1adf43dc3. Witness TOMCAT-REWRITE-NS-LOOP-WITNESS. Última linha:
SUCCESS TOMCAT-REWRITE-NS-LOOP control-ok=yes ns-hang=yes L-ns-safe=yes n-hang=yes skip-hang=yes sibling-alive=yes TOMCAT-REWRITE-NS-LOOP-WITNESS
rewrite.config do lab:
RewriteRule ^/loop$ - [NS]
RewriteRule ^/next$ - [N]
RewriteRule ^/skip$ - [S=-1]
RewriteRule ^/ok$ - [L]
RewriteRule ^/safe$ - [L,NS]
Limitar as rodadas de rewrite (MAX_REWRITE_ROUNDS=1000 no main após esta tag, commit 2da9fddb). Flags com correspondência exata para que [NS] falhe no parse (commit 8992dbdd). Rejeitar skip < 0. Até que isso chegue a um release: evite [NS]/[NV]/[N]/[S=-1] em regras correspondentes, ou remova a RewriteValve.
52f3b364e5882a5dbcfc42cb8313a7c2ae532c64)RewriteValve.java reinício de isNext() e startsWith("N")RewriteRule.java substituição - retorna a URL original