
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() と非 null の newtest が揃うと、ラウンド上限なしで i = -1 が設定されるため、ルールに一致する未認証の GET リクエスト1回で、クライアントが切断するかプロセスが再起動するまで HTTP コネクタのスレッドが固定される。明示的な [N] と [S=-1] も同じ固定を引き起こす。[L,NS] は isLast() が先に break するため安全である。
CVE-2026-65927 (Important, 11.0.25) は [N] が2番目のルールから再開する問題を修正した。この固定には依然としてラウンド上限がない。11.0.27 以降の同日の main では、セキュリティアドバイザリなしで MAX_REWRITE_ROUNDS=1000 と厳密なフラグ一致 (2da9fddb、8992dbdd) が追加された。
| 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 である。ラボは 127.0.0.1:18910 上のループバック HTTP である。公式の 11.0.27 tarball を使用。RCE ではない。
| 手法 | シンク | 権限 |
|---|---|---|
Host の rewrite.config の RewriteRule ^/loop$ - [NS] に一致する未認証 GET /loop | トークン NS に対する startsWith("N") により RewriteValve.invoke で i = -1 | 可用性 (ラボで実証): クライアントが切断するかプロセスが再起動するまで、HTTP コネクタのスレッド1本を固定する。ラボ: /loop は 4.002 秒でタイムアウトした。 |
明示的な [N] を伴う未認証 GET /next | 同じ isNext() による再開 | 可用性 (ラボで実証): 同じ固定。ラボ: /next は 4.003 秒でタイムアウトした。 |
[S=-1] を伴う未認証 GET /skip | i += -1 の後に for のインクリメント | 可用性 (ラボで実証): 同じ固定。ラボ: /skip は 4.002 秒でタイムアウトした。 |
| ワーカーごとに1つの一致リクエスト | 同じシンク、maxThreads 個の同時一致 URL | 可用性 (推定): コネクタプールを埋め尽くす (ラボでは 32 を使用、標準デフォルトは 200)。以降の新規リクエストはキューに入るか拒否される。1つの /loop が実行中でも、兄弟パスの /ok は応答し続けた。 |
/ok の 13ms の 404 に対するハング | next/skip と [L] のタイミング比較 | 偵察 (ラボで実証): next/skip ルールが一致したことを判別できる。ラボ: 4.002 秒のハングに対し 0.013 秒の 404。 |
ラボで実証済み vs 推定: /loop [NS] のハング、/next [N] のハング、/skip [S=-1] のハング、/ok [L] の 13ms での 404、/safe [L,NS] の 5ms での 404、および /loop 実行中に兄弟パスの /ok が 404 を返すことはラボで実証済みである。プール枯渇は、スレッド1本の固定と maxThreads から推定したものである。
必要条件: Host または Context 上の RewriteValve と、それに対応する rewrite.config ルール。Valve はオプトインであり、コメントアウトされた AJP と同じクラスに属する。コピーされた httpd の [NS] は一致ルールであり、意図的な無限 [N] ループではない。
| サーフェス | ラボ |
|---|---|
[L,NS] (httpd last+nosubreq) | /safe は 5ms で 404 (L-ns-safe=yes) |
| 1 つのスレッドが固定されている間の他の処理中リクエスト | /loop 中の /ok は 13ms で 404 (sibling-alive=yes) |
RewriteValve なしの標準 server.xml | valve は存在せず、残存物は発火しない |
| CVE-2026-65927 の RCE / WEB-INF 読み取り / 制約バイパス | [N] はルール 0 から再開するようになった。この残存物は可用性のみに影響する |
| Manager / JMX / AJP / DefaultServlet PUT | 変更なし。PUT は依然として readonly |
Tomcat ユーザーとしてのコード実行はなし。conf/ や WEB-INF の読み取りはなし。セッション窃取はなし。Manager デプロイはなし。JVM は稼働し続け、プールが埋まるまで他のワーカーは応答を続ける。
cd lab
./run.sh
ループバック 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]
書き換えラウンド数を上限付きにする(このタグ以降の main では MAX_REWRITE_ROUNDS=1000、コミット 2da9fddb)。完全一致フラグにより [NS] はパースに失敗する(コミット 8992dbdd)。skip < 0 を拒否する。これらがリリースに反映されるまでは、マッチングルールで [NS]/[NV]/[N]/[S=-1] を避けるか、RewriteValve を削除すること。
52f3b364e5882a5dbcfc42cb8313a7c2ae532c64)RewriteValve.java の isNext() 再起動と startsWith("N")RewriteRule.java の置換 - は元の URL を返す