
Apache Tomcat 11.0.27 — высокий — удалённый — DoS — RewriteValve [NS] worker pin
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · tomcat-rewrite-ns-loop
Разбор: https://abraxaslabs.tech/research/tomcat-rewrite-ns-loop
Класс: DoS Доступ: Удалённый
Apache Tomcat 11.0.27 (52f3b364) - tomcat.apache.org
RewriteValve.parseRuleFlag выполняет сопоставление по префиксу. После NC/NE любой оставшийся флаг, который startsWith("N"), включает next. Скопированные из httpd [NS] (nosubreq) и [NV] (novary) превращаются в [N]. Подстановка - оставляет substitution равным null; тогда RewriteRule.evaluate возвращает исходный URL. isNext() вместе с ненулевым newtest устанавливает i = -1 без ограничения числа раундов, поэтому один неаутентифицированный GET, совпадающий с правилом, намертво занимает поток HTTP-коннектора, пока клиент не отключится или процесс не будет перезапущен. Явные [N] и [S=-1] дают такое же зависание. [L,NS] безопасен, поскольку isLast() срабатывает первым.
CVE-2026-65927 (Important, 11.0.25) исправил перезапуск [N] со второго правила. У этого зависания по-прежнему нет ограничения числа раундов. В тот же день в main после 11.0.27 добавили MAX_REWRITE_ROUNDS=1000 и точное сопоставление флагов (2da9fddb, 8992dbdd) без security advisory.
| ID | CVE пока нет |
| Класс | DoS |
| Доступ | Удалённый (неаутентифицированный 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 |
| Продукт | Apache Tomcat |
| Затронуто | 11.0.27 (52f3b364e5882a5dbcfc42cb8313a7c2ae532c64) RewriteValve |
| Аутентификация | неаутентифицированный HTTP |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Я зафиксировал тег 11.0.27 и изучил RewriteValve.invoke. Этот valve отсутствует в стандартном conf/server.xml. Путь по умолчанию для Host — conf/Catalina/localhost/rewrite.config. Лаборатория — loopback HTTP на 127.0.0.1:18910. Официальный tarball 11.0.27. Это не RCE.
| Метод | Точка срабатывания | Привилегия |
|---|---|---|
Неаутентифицированный GET /loop, совпадающий с RewriteRule ^/loop$ - [NS] в rewrite.config Host | RewriteValve.invoke i = -1, поскольку startsWith("N") для токена NS | Доступность (подтверждено в лаборатории): занимает один поток HTTP-коннектора, пока клиент не отключится или процесс не будет перезапущен. Лаборатория: /loop завершился по таймауту за 4.002s. |
Неаутентифицированный GET /next с явным [N] | тот же перезапуск через isNext() | Доступность (подтверждено в лаборатории): такое же зависание. Лаборатория: /next завершился по таймауту за 4.003s. |
Неаутентифицированный GET /skip с [S=-1] | i += -1, затем инкремент for | Доступность (подтверждено в лаборатории): такое же зависание. Лаборатория: /skip завершился по таймауту за 4.002s. |
| Один совпадающий запрос на worker | та же точка срабатывания, maxThreads одновременных совпадающих URL | Доступность (предположительно): исчерпание пула коннектора (в лаборатории использовалось 32; стандартное значение по умолчанию — 200). Новые запросы затем встают в очередь или отклоняются. Соседний /ok всё ещё отвечал, пока один /loop был в обработке. |
Зависание против 404 за 13ms на /ok | тайминг next/skip против [L] | Разведка (подтверждено в лаборатории): позволяет определить, что совпало правило next/skip. Лаборатория: зависание 4.002s против 404 за 0.013s. |
Подтверждено в лаборатории vs предположительно: зависание /loop [NS], зависание /next [N], зависание /skip [S=-1], 404 на /ok [L] за 13ms, 404 на /safe [L,NS] за 5ms и 404 на соседнем /ok, пока /loop в обработке, — подтверждены в лаборатории. Исчерпание пула выведено из зависания одного потока плюс maxThreads.
Требуется: RewriteValve на Host или Context и соответствующее правило в rewrite.config. Valve включается опционально, тот же класс, что и закомментированный AJP. Скопированный из httpd [NS] — это совпадающее правило, а не намеренный бесконечный цикл [N].
| Поверхность | Лаборатория |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe 404 за 5мс (L-ns-safe=yes) |
| Другие запросы в обработке, пока один поток закреплён | /ok 404 за 13мс во время /loop (sibling-alive=yes) |
Штатный server.xml без RewriteValve | valve отсутствует; остаточное правило не срабатывает |
| RCE / чтение WEB-INF / обход ограничений CVE-2026-65927 | [N] теперь перезапускается с правила 0; это остаточное явление затрагивает только доступность |
| Manager / JMX / AJP / DefaultServlet PUT | без изменений; PUT по-прежнему readonly |
Никакого выполнения кода от имени пользователя Tomcat. Никакого чтения conf/ или WEB-INF. Никакого перехвата сессии. Никакого развёртывания через Manager. JVM продолжает работать; другие worker'ы отвечают, пока пул не заполнен.
cd lab
./run.sh
Loopback 127.0.0.1:18910 сопоставляется с контейнером 8080. Проект Compose tomcat-rewrite-ns-loop. Тег образа tomcat-rewrite-ns-loop:11.0.27. Официальный apache-tomcat-11.0.27.tar.gz SHA512 a617896307cada4884b24f80c4e977ddad8b3e0f88ce0e2a6eea29d5f332498e276ae5976c227a9c8daecf05c7c90def684a3766dd5a81f47a56d8b1adf43dc3. Свидетель TOMCAT-REWRITE-NS-LOOP-WITNESS. Последняя строка:
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]
Ограничение числа раундов перезаписи (MAX_REWRITE_ROUNDS=1000 в main после этого тега, коммит 2da9fddb). Флаги точного совпадения, чтобы [NS] не проходил парсинг (коммит 8992dbdd). Отклонение skip < 0. Пока эти изменения не вошли в релиз: избегайте [NS]/[NV]/[N]/[S=-1] в правилах сопоставления или откажитесь от RewriteValve.