localhost なら安全、ではない — Ray CVE-2025-62593 と DNS リバインディング
サーバの脆弱性は、たいてい「外に公開しているから危ない」という形をしている。だから対策も「公開範囲を絞る」で通じることが多い。
Ray の CVE-2025-62593 は、その直感が効かない側の脆弱性である。狙われるのは本番のクラスタではなく、手元で ray を立ち上げたまま Web を見ている開発者の端末だ。しかも待ち受けが localhost だけでも成立する。
CISA の KEV(既知の悪用された脆弱性カタログ)に 2026-08-17 に収載され、是正期限は 2026-08-20。猶予は3日しかない。
この記事では、なぜ localhost が安全にならないのかを仕組みから読み解き、最後に自分の環境が該当するかを依存なしで判定するスクリプトを置く。
何が起きるのか
影響を受けるのは Ray 2.52.0 より前のすべてのバージョン、修正版は 2.52.0 である。NVD の CPE も versionEndExcluding: 2.52.0 になっている。
Ray はブラウザ由来の攻撃を防ぐため、リクエストの User-Agent ヘッダが文字列 Mozilla で始まるかどうかを見ていた。ブラウザからのリクエストなら弾く、という発想である。
この防御が成り立たない理由は1行で書ける。fetch の仕様では User-Agent を書き換えられる。 つまり攻撃者のページから Mozilla で始まらない User-Agent を付けたリクエストを出せば、この検査は素通りする。
ここに DNS リバインディングが組み合わさる。
なぜ localhost でも届くのか
DNS リバインディングは、ブラウザの同一オリジンポリシーを名前解決の側から迂回する古典的な手口である。手順はこうなる。
- 被害者が攻撃者のページ
http://attacker.exampleを開く。この時点でattacker.exampleは攻撃者のサーバの IP を指している。 - その DNS レコードの TTL は極端に短く設定されている。
- ページ内の JavaScript がしばらく待つ。その間に攻撃者は
attacker.exampleの A レコードを127.0.0.1に差し替える。 - 同じページから
http://attacker.example:8265/...へリクエストを出す。ブラウザから見ればオリジンは変わっていないので同一オリジンポリシーには引っかからない。しかし実際の接続先は被害者自身の localhost である。
ここで要点になるのが、ファイアウォールでは止まらないということだ。パケットは外から入ってくるのではなく、被害者の端末の中で完結している。「Ray を localhost にしか bind していないから外からは触れない」という前提が、この手口の前では成立しない。
被害者側に必要な操作は「悪意あるページを開く」だけである。不正広告(マルバタイジング)を経由すれば、閲覧していたのが普通のサイトでも成立しうる。NVD の CVSS 3.1 が UI:R、CNA(GitHub)の CVSS 4.0 が UI:P(Passive)になっているのは、この「能動的な操作を要さない」性質を反映している。
CVSS の評価は分かれている。
| 評価元 | バージョン | スコア | 深刻度 |
|---|---|---|---|
| NVD(Primary) | CVSS 3.1 | 8.8 | High |
| GitHub Security Advisories(CNA) | CVSS 4.0 | 9.4 | Critical |
差の中心は利用者の関与の重み付けで、攻撃像そのものは一致している。CWE は CWE-94(コードインジェクション) と CWE-352(CSRF) の2つが割り当てられている。
資産管理台帳では見つからない
対応で最初につまずくのはここである。「本番に Ray クラスタは無いので対象外」と結論すると外す。この脆弱性の主戦場は開発者の端末だからだ。
確認すべきなのは次の2点になる。
- 開発端末に入っている Ray のバージョンが 2.52.0 未満か
- Ray の待ち受けが動いていないか(localhost であっても)
1 が厄介なのは、Ray が venv や conda 環境ごとに入る Python パッケージだという点である。pip show ray を1回叩いて終わりにすると、別の仮想環境に古い Ray が残る。ディストリのセキュリティ更新でも上がらない。
判定スクリプト
外部ライブラリを使わず、標準ライブラリだけで動く。ホームディレクトリ以下の仮想環境を走査して、ray の dist-info からバージョンを読み、2.52.0 と比較する。あわせて Ray が使いがちなポートの待ち受けも見る。
1 | |
実行する。
1 | |
出力はこうなる。
1 | |
[listening] が出た場合、そのポートは DNS リバインディングの到達先になりうる。localhost だから安全という判断はしないこと。
補足を2つ。dist-info を見ているのは、import ray せずにバージョンを読むためである。壊れた環境や別アーキテクチャ向けの環境でも落ちない。またポート判定は TCP 接続の可否だけを見ているので、Ray 以外のプロセスが同じポートを使っていれば当然そちらも拾う。ポート番号は環境で変わるため、ray start の引数に合わせて PORTS を調整してほしい。
対応
恒久対策は 2.52.0 以降への更新の一本である。User-Agent に依存した旧来の防御に戻す選択肢は無い。
1 | |
すぐに更新できない場合の緩和は、いずれも「悪意あるページを踏む確率を下げる」方向のもので、脆弱性自体は残る点を理解して運用する。
- 作業していないときは Ray のプロセスを落とす
- 開発用のブラウザと日常のブラウジングを分ける
- Ray を動かす端末で広告の表示を抑える
そして、更新前に開発者が任意の Web を閲覧していた端末は、侵害を前提に点検する。KEV に載っているということは、実際に悪用が確認されているという意味である。見るべきは次のあたりになる。
- Ray のワーカープロセスから起動された想定外の子プロセス
- シェル履歴と対応しないネットワーク接続
- 端末に置かれたクラウド資格情報(
~/.aws/credentials、gcloud の ADC、~/.kube/config)へのアクセス痕跡
開発端末は本番への鍵を持っていることが多い。端末側の RCE が、そのまま本番の資格情報の流出になる。点検して終わりではなく、該当端末に置かれていた資格情報は再発行するところまでが完了条件である。
まとめ
- 影響は Ray 2.52.0 未満のすべて。修正版は 2.52.0
- 防御が
User-Agentの先頭一致だったため、fetch から迂回できた - DNS リバインディングと組み合わさるので、localhost 限定の待ち受けでも到達する
- ファイアウォールでは止まらない。更新以外に恒久対策は無い
- KEV 収載 2026-08-17、是正期限 2026-08-20
CVE の一次情報と各クラウドでの対応状況は CVE Watch にまとめている。