
Apache Tomcat 11.0.27 - Hoch - Remote - 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
Klasse: DoS Reichweite: Remote
Apache Tomcat 11.0.27 (52f3b364) - tomcat.apache.org
RewriteValve.parseRuleFlag verwendet Präfix-Matching. Nach NC/NE aktiviert jedes verbleibende Flag, das startsWith("N") erfüllt, das nächste. Die aus httpd übernommenen Flags [NS] (nosubreq) und [NV] (novary) werden zu [N]. Die Substitution - lässt substitution null; RewriteRule.evaluate gibt dann die ursprüngliche URL zurück. isNext() zusammen mit einem nicht-null newtest setzt i = -1 ohne Begrenzung der Runden, sodass ein einziger nicht authentifizierter GET, der die Regel trifft, den Thread des HTTP-Connectors blockiert, bis der Client die Verbindung trennt oder der Prozess neu gestartet wird. Explizites [N] und [S=-1] bewirken dieselbe Blockade. [L,NS] ist sicher, weil isLast() zuerst abbricht.
CVE-2026-65927 (Important, 11.0.25) behob, dass [N] bei der zweiten Regel neu startete. Diese Blockade hat weiterhin keine Begrenzung der Runden. Der noch am selben Tag nach 11.0.27 aktualisierte main-Branch fügte MAX_REWRITE_ROUNDS=1000 und exaktes Flag-Matching hinzu (2da9fddb, 8992dbdd), ohne Security Advisory.
| ID | noch keine CVE |
| Klasse | DoS |
| Reichweite | Remote (nicht authentifiziertes HTTP) |
| 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 |
| Produkt | Apache Tomcat |
| Betroffen | 11.0.27 (52f3b364e5882a5dbcfc42cb8313a7c2ae532c64) RewriteValve |
| Auth | nicht authentifiziertes HTTP |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Ich habe den Tag 11.0.27 gepinnt und RewriteValve.invoke gelesen. Das Valve ist nicht im Standard-conf/server.xml enthalten. Der Standardpfad des Host ist conf/Catalina/localhost/rewrite.config. Das Lab ist Loopback-HTTP auf 127.0.0.1:18910. Offizielles 11.0.27-Tarball. Kein RCE.
| Methode | Sink | Privileg |
|---|---|---|
Unauth GET /loop, das die Host-rewrite.config-Regel RewriteRule ^/loop$ - [NS] trifft | RewriteValve.invoke i = -1 wegen startsWith("N") beim Token NS | Verfügbarkeit (im Lab nachgewiesen): einen HTTP-Connector-Thread blockieren, bis der Client die Verbindung trennt oder der Prozess neu gestartet wird. Lab: /loop lief nach 4,002s in einen Timeout. |
Unauth GET /next mit explizitem [N] | derselbe isNext()-Neustart | Verfügbarkeit (im Lab nachgewiesen): dieselbe Blockade. Lab: /next lief nach 4,003s in einen Timeout. |
Unauth GET /skip mit [S=-1] | i += -1, dann der for-Inkrement | Verfügbarkeit (im Lab nachgewiesen): dieselbe Blockade. Lab: /skip lief nach 4,002s in einen Timeout. |
| Eine passende Anfrage pro Worker | derselbe Sink, maxThreads gleichzeitige passende URLs | Verfügbarkeit (abgeleitet): den Connector-Pool füllen (im Lab 32 verwendet; Standard ist 200). Neue Anfragen werden dann in die Warteschlange gestellt oder abgelehnt. Die Schwester-Route /ok antwortete weiterhin, während ein /loop in Bearbeitung war. |
Hängen vs. 13ms 404 auf /ok | Timing von next/skip vs. [L] | Recon (im Lab nachgewiesen): erkennen, dass eine next/skip-Regel getroffen wurde. Lab: 4,002s Hängen vs. 0,013s 404. |
Im Lab nachgewiesen vs. abgeleitet: Das Hängen von /loop [NS], das Hängen von /next [N], das Hängen von /skip [S=-1], /ok [L] 404 in 13ms, /safe [L,NS] 404 in 5ms und die Schwester-Route /ok 404, während /loop in Bearbeitung ist, sind im Lab nachgewiesen. Die Pool-Erschöpfung ist aus der Blockade eines Threads plus maxThreads abgeleitet.
Erfordert: RewriteValve auf dem Host oder Context und eine passende rewrite.config-Regel. Das Valve ist Opt-in, gleiche Klasse wie das auskommentierte AJP. Das kopierte httpd [NS] ist eine passende Regel, keine beabsichtigte Endlos-[N]-Schleife.
| Oberfläche | Lab |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe 404 in 5ms (L-ns-safe=yes) |
| Andere laufende Anfragen, während ein Thread festgehalten wird | /ok 404 in 13ms während /loop (sibling-alive=yes) |
Standard-server.xml ohne RewriteValve | Valve fehlt; der Rest feuert nicht |
| RCE / WEB-INF-Lesen / Constraint-Bypass von CVE-2026-65927 | [N] startet jetzt bei Regel 0 neu; dieser Rest betrifft nur die Verfügbarkeit |
| Manager / JMX / AJP / DefaultServlet PUT | unverändert; PUT weiterhin readonly |
Keine Codeausführung als Tomcat-Benutzer. Kein Lesen von conf/ oder WEB-INF. Kein Session-Diebstahl. Kein Manager-Deploy. Die JVM bleibt oben; andere Worker antworten weiterhin, bis der Pool gefüllt ist.
cd lab
./run.sh
Loopback 127.0.0.1:18910 bildet Container-8080 ab. Compose-Projekt tomcat-rewrite-ns-loop. Image-Tag tomcat-rewrite-ns-loop:11.0.27. Offizielles apache-tomcat-11.0.27.tar.gz SHA512 a617896307cada4884b24f80c4e977ddad8b3e0f88ce0e2a6eea29d5f332498e276ae5976c227a9c8daecf05c7c90def684a3766dd5a81f47a56d8b1adf43dc3. Witness TOMCAT-REWRITE-NS-LOOP-WITNESS. Letzte Zeile:
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]
Begrenzung der Rewrite-Runden (MAX_REWRITE_ROUNDS=1000 auf main nach diesem Tag, Commit 2da9fddb). Exact-Match-Flags, sodass [NS] beim Parsen fehlschlägt (Commit 8992dbdd). skip < 0 wird abgelehnt. Bis diese Änderungen in einem Release enthalten sind: Vermeiden Sie [NS]/[NV]/[N]/[S=-1] in Matching-Regeln oder entfernen Sie das RewriteValve.