
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
Write-up: https://abraxaslabs.tech/research/tomcat-rewrite-ns-loop
Classe: DoS Portata: Remota
Apache Tomcat 11.0.27 (52f3b364) - tomcat.apache.org
RewriteValve.parseRuleFlag esegue un prefix-match. Dopo NC/NE, qualsiasi flag rimanente che inizia con "N" (startsWith("N")) abilita il successivo. Le direttive httpd copiate [NS] (nosubreq) e [NV] (novary) diventano [N]. La sostituzione - lascia substitution null; RewriteRule.evaluate restituisce quindi l'URL originale. isNext() insieme a un newtest non nullo imposta i = -1 senza alcun limite di cicli, quindi una singola GET non autenticata che corrisponde alla regola blocca il thread del connettore HTTP finché il client non si disconnette o il processo non viene riavviato. [N] esplicito e [S=-1] producono lo stesso blocco. [L,NS] è sicuro perché isLast() interrompe prima.
CVE-2026-65927 (Important, 11.0.25) ha corretto il riavvio di [N] dalla seconda regola. Questo blocco è ancora privo di un limite di cicli. Il main dello stesso giorno successivo alla 11.0.27 ha aggiunto MAX_REWRITE_ROUNDS=1000 e la corrispondenza esatta dei flag (2da9fddb, 8992dbdd) senza un avviso di sicurezza.
| ID | nessuna CVE al momento |
| Classe | DoS |
| Portata | Remota (HTTP non autenticato) |
| CWE | CWE-835 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| Prodotto | Apache Tomcat |
| Affetto | 11.0.27 (52f3b364e5882a5dbcfc42cb8313a7c2ae532c64) RewriteValve |
| Auth | HTTP non autenticato |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Ho fissato il tag 11.0.27 e analizzato RewriteValve.invoke. La valve non è presente nel conf/server.xml predefinito. Il percorso predefinito dell'Host è conf/Catalina/localhost/rewrite.config. Il lab è HTTP su loopback su 127.0.0.1:18910. Tarball ufficiale 11.0.27. Non è RCE.
| Metodo | Sink | Privilegio |
|---|---|---|
GET /loop non autenticata che corrisponde alla RewriteRule ^/loop$ - [NS] dell'Host rewrite.config | RewriteValve.invoke i = -1 perché startsWith("N") sul token NS | Disponibilità (dimostrata nel lab): blocca un thread del connettore HTTP finché il client non si disconnette o il processo non viene riavviato. Lab: /loop è andato in timeout a 4.002s. |
GET /next non autenticata con [N] esplicito | stesso riavvio di isNext() | Disponibilità (dimostrata nel lab): stesso blocco. Lab: /next è andato in timeout a 4.003s. |
GET /skip non autenticata con [S=-1] | i += -1 poi l'incremento del for | Disponibilità (dimostrata nel lab): stesso blocco. Lab: /skip è andato in timeout a 4.002s. |
| Una richiesta corrispondente per worker | stesso sink, maxThreads URL corrispondenti concorrenti | Disponibilità (dedotta): saturare il pool del connettore (il lab ne usava 32; il valore predefinito standard è 200). Le nuove richieste vengono quindi messe in coda o rifiutate. La richiesta gemella /ok ha comunque risposto mentre una /loop era in corso. |
Blocco vs 404 in 13ms su /ok | temporizzazione di next/skip vs [L] | Ricognizione (dimostrata nel lab): rilevare che una regola next/skip ha trovato corrispondenza. Lab: blocco di 4.002s vs 404 in 0.013s. |
Dimostrato nel lab vs dedotto: il blocco di /loop [NS], il blocco di /next [N], il blocco di /skip [S=-1], il 404 di /ok [L] in 13ms, il 404 di /safe [L,NS] in 5ms e il 404 della richiesta gemella /ok mentre /loop è in corso sono dimostrati nel lab. L'esaurimento del pool è dedotto dal blocco di un singolo thread più maxThreads.
Richiede: RewriteValve sull'Host o sul Context e una regola corrispondente in rewrite.config. La valve è opt-in, stessa classe dell'AJP commentato. L'[NS] copiato da httpd è una regola di corrispondenza, non un loop infinito [N] intenzionale.
| Superficie | Lab |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe 404 in 5ms (L-ns-safe=yes) |
| Altre richieste in-flight mentre un thread è bloccato | /ok 404 in 13ms durante /loop (sibling-alive=yes) |
server.xml stock senza RewriteValve | valve assente; il leftover non si attiva |
| RCE / lettura WEB-INF / bypass dei constraint di CVE-2026-65927 | [N] ora riparte dalla regola 0; questo leftover riguarda solo la disponibilità |
| Manager / JMX / AJP / DefaultServlet PUT | invariati; PUT resta readonly |
Nessuna esecuzione di codice come utente Tomcat. Nessuna lettura di conf/ o WEB-INF. Nessun furto di sessione. Nessun deploy tramite Manager. La JVM resta attiva; gli altri worker rispondono ancora finché il pool non è saturo.
cd lab
./run.sh
Loopback 127.0.0.1:18910 mappa il container 8080. Progetto Compose tomcat-rewrite-ns-loop. Tag immagine tomcat-rewrite-ns-loop:11.0.27. SHA512 ufficiale di apache-tomcat-11.0.27.tar.gz a617896307cada4884b24f80c4e977ddad8b3e0f88ce0e2a6eea29d5f332498e276ae5976c227a9c8daecf05c7c90def684a3766dd5a81f47a56d8b1adf43dc3. Witness TOMCAT-REWRITE-NS-LOOP-WITNESS. Ultima riga:
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
Lab rewrite.config:
RewriteRule ^/loop$ - [NS]
RewriteRule ^/next$ - [N]
RewriteRule ^/skip$ - [S=-1]
RewriteRule ^/ok$ - [L]
RewriteRule ^/safe$ - [L,NS]
Limita i cicli di riscrittura (MAX_REWRITE_ROUNDS=1000 su main dopo questo tag, commit 2da9fddb). Flag di corrispondenza esatta affinché [NS] fallisca il parsing (commit 8992dbdd). Rifiuta skip < 0. Finché queste modifiche non arrivano in una release: evita [NS]/[NV]/[N]/[S=-1] sulle regole di corrispondenza, oppure rimuovi RewriteValve.