
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 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 copiate da httpd [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 null 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 [N] che riavviava dalla seconda regola. Questo blocco è ancora privo di un limite di cicli. Il main dello stesso giorno dopo 11.0.27 ha aggiunto MAX_REWRITE_ROUNDS=1000 e il matching esatto dei flag (2da9fddb, 8992dbdd) senza un security advisory.
| 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 letto RewriteValve.invoke. La valve non è presente nel conf/server.xml di default. 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 in 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 isNext() | Disponibilità (dimostrata in 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 in lab): stesso blocco. Lab: /skip è andato in timeout a 4.002s. |
| Una richiesta corrispondente per worker | stesso sink, maxThreads URL corrispondenti concorrenti | Disponibilità (inferita): satura il pool del connettore (il lab ne usava 32; il default di serie è 200). Le nuove richieste vengono quindi messe in coda o rifiutate. La richiesta sorella /ok ha comunque risposto mentre una /loop era in corso. |
Blocco vs 404 in 13ms su /ok | timing di next/skip vs [L] | Recon (dimostrata in lab): rileva che una regola next/skip ha trovato corrispondenza. Lab: blocco di 4.002s vs 404 in 0.013s. |
Dimostrato in lab vs inferito: il blocco di /loop [NS], il blocco di /next [N], il blocco di /skip [S=-1], /ok [L] 404 in 13ms, /safe [L,NS] 404 in 5ms e la richiesta sorella /ok 404 mentre /loop è in corso sono dimostrati in lab. L'esaurimento del pool è inferito dal blocco di un 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. La [NS] copiata da httpd è una regola corrispondente, non un loop [N] infinito intenzionale.
| Superficie | Lab |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe 404 in 5ms (L-ns-safe=yes) |
| Altre richieste in corso mentre un thread è bloccato | /ok 404 in 13ms durante /loop (sibling-alive=yes) |
server.xml di serie senza RewriteValve | valve assente; il residuo non si attiva |
| RCE / lettura di WEB-INF / bypass dei vincoli di CVE-2026-65927 | [N] ora riavvia dalla regola 0; questo residuo è solo 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 è saturato.
cd lab
./run.sh
Loopback 127.0.0.1:18910 mappa la porta 8080 del container. Progetto Compose tomcat-rewrite-ns-loop. Tag immagine tomcat-rewrite-ns-loop:11.0.27. apache-tomcat-11.0.27.tar.gz ufficiale SHA512 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
rewrite.config del lab:
RewriteRule ^/loop$ - [NS]
RewriteRule ^/next$ - [N]
RewriteRule ^/skip$ - [S=-1]
RewriteRule ^/ok$ - [L]
RewriteRule ^/safe$ - [L,NS]
Limita i cicli di rewrite (MAX_REWRITE_ROUNDS=1000 su main dopo questo tag, commit 2da9fddb). Flag con match esatto in modo che [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 corrispondenti, oppure rimuovi RewriteValve.
52f3b364e5882a5dbcfc42cb8313a7c2ae532c64)RewriteValve.java riavvio isNext() e startsWith("N")RewriteRule.java la sostituzione - restituisce l'URL originale