
Apache Tomcat 11.0.27 - Alta - Remota - DoS - RewriteValve [NS] worker pin
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · tomcat-rewrite-ns-loop
Clase: DoS Alcance: Remoto
Apache Tomcat 11.0.27 (52f3b364) - tomcat.apache.org
RewriteValve.parseRuleFlag hace coincidencia por prefijo. Después de NC/NE, cualquier flag restante que startsWith("N") habilita next. Los [NS] (nosubreq) y [NV] (novary) copiados de httpd se convierten en [N]. La sustitución - deja substitution en null; RewriteRule.evaluate entonces devuelve la URL original. isNext() más un newtest no nulo establece i = -1 sin límite de rondas, por lo que un único GET no autenticado que coincida con la regla fija el hilo del conector HTTP hasta que el cliente se desconecte o el proceso se reinicie. [N] explícito y [S=-1] son el mismo pin. [L,NS] es seguro porque isLast() rompe primero.
CVE-2026-65927 (Important, 11.0.25) corrigió que [N] reiniciara en la segunda regla. Este pin todavía no tiene límite de rondas. El main del mismo día posterior a 11.0.27 añadió MAX_REWRITE_ROUNDS=1000 y coincidencia exacta de flags (2da9fddb, 8992dbdd) sin un aviso de seguridad.
| ID | sin CVE todavía |
| Clase | DoS |
| Alcance | Remoto (HTTP no autenticado) |
| CWE | CWE-835 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| Producto | Apache Tomcat |
| Afectado | 11.0.27 (52f3b364e5882a5dbcfc42cb8313a7c2ae532c64) RewriteValve |
| Auth | HTTP no autenticado |
| Licencia | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Fijé el tag 11.0.27 y leí RewriteValve.invoke. La valve no está en el conf/server.xml de serie. La ruta por defecto del Host es conf/Catalina/localhost/rewrite.config. El lab es HTTP en loopback sobre 127.0.0.1:18910. Tarball oficial de 11.0.27. No es RCE.
| Método | Sink | Privilegio |
|---|---|---|
GET /loop no autenticado que coincide con RewriteRule ^/loop$ - [NS] del Host rewrite.config | RewriteValve.invoke i = -1 porque startsWith("N") sobre el token NS | Disponibilidad (probado en lab): fija un hilo del conector HTTP hasta que el cliente se desconecte o el proceso se reinicie. Lab: /loop agotó el tiempo de espera a los 4.002s. |
GET /next no autenticado con [N] explícito | mismo reinicio de isNext() | Disponibilidad (probado en lab): mismo pin. Lab: /next agotó el tiempo de espera a los 4.003s. |
GET /skip no autenticado con [S=-1] | i += -1 y luego el incremento del for | Disponibilidad (probado en lab): mismo pin. Lab: /skip agotó el tiempo de espera a los 4.002s. |
| Una petición coincidente por worker | mismo sink, maxThreads URLs coincidentes concurrentes | Disponibilidad (inferido): llena el pool del conector (el lab usó 32; el valor por defecto de serie es 200). Las nuevas peticiones entonces se encolan o se rechazan. El hermano /ok siguió respondiendo mientras un /loop estaba en vuelo. |
Cuelgue vs 404 de 13ms en /ok | temporización de next/skip vs [L] | Recon (probado en lab): detectar que una regla next/skip coincidió. Lab: cuelgue de 4.002s vs 404 de 0.013s. |
Probado en lab vs inferido: el cuelgue de /loop [NS], el cuelgue de /next [N], el cuelgue de /skip [S=-1], el 404 de /ok [L] en 13ms, el 404 de /safe [L,NS] en 5ms, y el 404 del hermano /ok mientras /loop está en vuelo están probados en lab. El agotamiento del pool se infiere del pin de un hilo más maxThreads.
Requiere: RewriteValve en el Host o Context y una regla coincidente en rewrite.config. La valve es opt-in, misma clase que el AJP comentado. El [NS] copiado de httpd es una regla coincidente, no un bucle [N] infinito intencional.
| Superficie | Lab |
|---|---|
[L,NS] (last+nosubreq de httpd) | /safe 404 en 5ms (L-ns-safe=yes) |
| Otras peticiones en vuelo mientras un hilo está fijado | /ok 404 en 13ms durante /loop (sibling-alive=yes) |
server.xml de serie sin RewriteValve | valve ausente; el remanente no se dispara |
| RCE / lectura de WEB-INF / bypass de restricciones de CVE-2026-65927 | [N] ahora reinicia en la regla 0; este remanente es solo de disponibilidad |
| Manager / JMX / AJP / PUT de DefaultServlet | sin cambios; PUT sigue en readonly |
Sin ejecución de código como el usuario de Tomcat. Sin lectura de conf/ ni WEB-INF. Sin robo de sesión. Sin despliegue vía Manager. La JVM sigue en pie; otros workers siguen respondiendo hasta que se llene el pool.
cd lab
./run.sh
Loopback 127.0.0.1:18910 mapea el contenedor 8080. Proyecto de Compose tomcat-rewrite-ns-loop. Tag de imagen tomcat-rewrite-ns-loop:11.0.27. apache-tomcat-11.0.27.tar.gz oficial SHA512 a617896307cada4884b24f80c4e977ddad8b3e0f88ce0e2a6eea29d5f332498e276ae5976c227a9c8daecf05c7c90def684a3766dd5a81f47a56d8b1adf43dc3. Testigo TOMCAT-REWRITE-NS-LOOP-WITNESS. Última línea:
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]
Limitar las rondas de rewrite (MAX_REWRITE_ROUNDS=1000 en main posterior a este tag, commit 2da9fddb). Flags con coincidencia exacta para que [NS] falle el parseo (commit 8992dbdd). Rechazar skip < 0. Hasta que eso llegue a una release: evitar [NS]/[NV]/[N]/[S=-1] en reglas coincidentes, o eliminar RewriteValve.
52f3b364e5882a5dbcfc42cb8313a7c2ae532c64)RewriteValve.java reinicio de isNext() y startsWith("N")RewriteRule.java la sustitución - devuelve la URL original