
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
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 le suivant. 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() plus un newtest non nul positionne i = -1 sans plafond de tours, donc un seul GET non authentifié qui correspond à 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 Host par défaut 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 de chute | Privilège |
|---|---|---|
GET /loop non authentifié correspondant à la règle Host rewrite.config RewriteRule ^/loop$ - [NS] | RewriteValve.invoke i = -1 car startsWith("N") sur le token NS | Disponibilité (prouvé en lab) : bloque un thread du connecteur HTTP jusqu'à ce que le client se déconnecte ou que le processus redémarre. Lab : /loop a expiré à 4,002 s. |
GET /next non authentifié avec [N] explicite | même redémarrage isNext() | Disponibilité (prouvé en lab) : même blocage. Lab : /next a expiré à 4,003 s. |
GET /skip non authentifié avec [S=-1] | i += -1 puis l'incrément du for | Disponibilité (prouvé en lab) : même blocage. Lab : /skip a expiré à 4,002 s. |
| Une requête correspondante par worker | même point de chute, 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 13 ms sur /ok | timing de next/skip vs [L] | Reconnaissance (prouvé en lab) : détecter qu'une règle next/skip a correspondu. Lab : blocage de 4,002 s vs 404 en 0,013 s. |
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 13 ms, le 404 de /safe [L,NS] en 5 ms, 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 plus maxThreads.
Nécessite : RewriteValve sur le Host ou le Context et une règle rewrite.config correspondante. La valve est optionnelle, de la même classe que l'AJP commenté. Le [NS] httpd copié est une règle correspondante, pas une boucle [N] infinie intentionnelle.
| Surface | Lab |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe 404 en 5 ms (L-ns-safe=yes) |
| Autres requêtes en cours pendant qu'un thread est bloqué | /ok 404 en 13 ms pendant /loop (sibling-alive=yes) |
server.xml d'origine sans RewriteValve | valve absente ; le reliquat 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 reliquat n'affecte que la disponibilité |
| Manager / JMX / AJP / PUT de DefaultServlet | inchangé ; PUT toujours readonly |
Pas d'exécution de code en tant qu'utilisateur Tomcat. Pas de lecture de conf/ ou WEB-INF. Pas de vol de session. Pas de déploiement via Manager. La JVM reste active ; les autres workers répondent encore jusqu'à ce que le pool soit rempli.
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
rewrite.config du lab :
RewriteRule ^/loop$ - [NS]
RewriteRule ^/next$ - [N]
RewriteRule ^/skip$ - [S=-1]
RewriteRule ^/ok$ - [L]
RewriteRule ^/safe$ - [L,NS]
Plafonner les tours de réécriture (MAX_REWRITE_ROUNDS=1000 sur main après ce tag, commit 2da9fddb). Correspondance exacte des indicateurs pour que [NS] échoue à l'analyse (commit 8992dbdd). Rejeter skip < 0. En attendant que ces correctifs sortent dans une version : éviter [NS]/[NV]/[N]/[S=-1] sur les règles correspondantes, ou supprimer RewriteValve.
52f3b364e5882a5dbcfc42cb8313a7c2ae532c64)RewriteValve.java redémarrage isNext() et startsWith("N")RewriteRule.java la substitution - renvoie l'URL d'origine