
Apache Tomcat 11.0.27 - Élevé - À distance - 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 Portée : Distante
Apache Tomcat 11.0.27 (52f3b364) - tomcat.apache.org
RewriteValve.parseRuleFlag effectue une correspondance par préfixe. Après NC/NE, tout indicateur restant qui startsWith("N") active next. Les indicateurs httpd copiés [NS] (nosubreq) et [NV] (novary) deviennent [N]. La substitution - laisse substitution à null ; RewriteRule.evaluate renvoie alors l'URL d'origine. isNext() combiné à un newtest non nul fixe i = -1 sans plafond de tours, de sorte qu'un seul GET non authentifié correspondant à la règle bloque le thread du connecteur HTTP jusqu'à ce que le client se déconnecte ou que le processus redémarre. [N] explicite et [S=-1] produisent le même blocage. [L,NS] est sûr car isLast() interrompt en premier.
CVE-2026-65927 (Important, 11.0.25) corrigeait le redémarrage de [N] à la deuxième règle. Ce blocage n'a toujours pas de plafond de tours. Le main du même jour après 11.0.27 a ajouté MAX_REWRITE_ROUNDS=1000 et une correspondance exacte des indicateurs (2da9fddb, 8992dbdd) sans avis de sécurité.
| ID | pas encore de CVE |
| Classe | DoS |
| Portée | Distante (HTTP non authentifié) |
| CWE | CWE-835 |
| CVSS | Élevé : 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| Produit | Apache Tomcat |
| Affecté | 11.0.27 (52f3b364e5882a5dbcfc42cb8313a7c2ae532c64) RewriteValve |
| Auth | HTTP non authentifié |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
J'ai épinglé le tag 11.0.27 et lu RewriteValve.invoke. La valve n'est pas présente dans le conf/server.xml d'origine. Le chemin par défaut du Host est conf/Catalina/localhost/rewrite.config. Le lab est en HTTP loopback sur 127.0.0.1:18910. Archive officielle 11.0.27. Pas de RCE.
| Méthode | Point d'impact | Privilège |
|---|---|---|
GET /loop non authentifié correspondant à la RewriteRule ^/loop$ - [NS] du rewrite.config du Host | RewriteValve.invoke i = -1 car startsWith("N") sur le token NS | Disponibilité (prouvé en lab) : bloquer un thread du connecteur HTTP jusqu'à ce que le client se déconnecte ou que le processus redémarre. Lab : /loop a expiré à 4,002s. |
GET /next non authentifié avec [N] explicite | même redémarrage isNext() | Disponibilité (prouvé en lab) : même blocage. Lab : /next a expiré à 4,003s. |
GET /skip non authentifié avec [S=-1] | i += -1 puis l'incrément de la boucle for | Disponibilité (prouvé en lab) : même blocage. Lab : /skip a expiré à 4,002s. |
| Une requête correspondante par worker | même point d'impact, maxThreads URL correspondantes simultanées | Disponibilité (déduit) : saturer le pool du connecteur (le lab en utilisait 32 ; la valeur par défaut d'origine est 200). Les nouvelles requêtes sont alors mises en file ou refusées. Le /ok voisin répondait toujours pendant qu'un /loop était en cours. |
Blocage vs 404 en 13ms sur /ok | chronométrage de next/skip vs [L] | Reconnaissance (prouvé en lab) : détecter qu'une règle next/skip a correspondu. Lab : blocage de 4,002s vs 404 en 0,013s. |
Prouvé en lab vs déduit : le blocage de /loop [NS], le blocage de /next [N], le blocage de /skip [S=-1], le 404 de /ok [L] en 13ms, le 404 de /safe [L,NS] en 5ms, et le 404 du /ok voisin pendant qu'un /loop est en cours sont prouvés en lab. L'épuisement du pool est déduit du blocage d'un thread combiné à maxThreads.
Nécessite : RewriteValve sur le Host ou le Context et une règle rewrite.config correspondante. La valve est opt-in, de la même classe que l'AJP commenté. Le [NS] copié d'httpd est une règle de correspondance, pas une boucle [N] infinie intentionnelle.
| Surface | Lab |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe 404 en 5ms (L-ns-safe=yes) |
| Autres requêtes en cours pendant qu'un thread est épinglé | /ok 404 en 13ms pendant /loop (sibling-alive=yes) |
server.xml d'origine sans RewriteValve | valve absente ; le résidu ne se déclenche pas |
| RCE / lecture de WEB-INF / contournement de contrainte de CVE-2026-65927 | [N] redémarre désormais à la règle 0 ; ce résidu est uniquement une atteinte à la disponibilité |
| Manager / JMX / AJP / PUT de DefaultServlet | inchangé ; PUT toujours readonly |
Aucune exécution de code en tant qu'utilisateur Tomcat. Aucune lecture de conf/ ou WEB-INF. Aucun vol de session. Aucun déploiement via Manager. La JVM reste active ; les autres workers répondent toujours jusqu'à saturation du pool.
cd lab
./run.sh
Loopback 127.0.0.1:18910 mappe le conteneur 8080. Projet Compose tomcat-rewrite-ns-loop. Tag d'image tomcat-rewrite-ns-loop:11.0.27. apache-tomcat-11.0.27.tar.gz officiel SHA512 a617896307cada4884b24f80c4e977ddad8b3e0f88ce0e2a6eea29d5f332498e276ae5976c227a9c8daecf05c7c90def684a3766dd5a81f47a56d8b1adf43dc3. Témoin TOMCAT-REWRITE-NS-LOOP-WITNESS. Dernière ligne :
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]
Limiter les cycles de réécriture (MAX_REWRITE_ROUNDS=1000 sur main après ce tag, commit 2da9fddb). Des indicateurs à correspondance exacte pour que [NS] échoue à l'analyse (commit 8992dbdd). Rejeter skip < 0. Tant que ces correctifs ne sont pas publiés dans une version : évitez [NS]/[NV]/[N]/[S=-1] sur les règles de correspondance, ou supprimez RewriteValve.