AWS のクラウドサービスをローカルで模す開発ツール LocalStack に、CloudFormation の deploy GUI を悪用する脆弱性 CVE-2025-65200 が見つかりました。細工した URL を開発者が開くと、deploy ページで反射型 XSS が成立し、注入された JavaScript が LocalStack の API から Lambda を作成・実行して、Docker コンテナ内でコードが走る、という連鎖です。修正は deploy GUI の撤去で、4.9.3 で入りました。この記事は、分類が割れている点を「確定した事実」と「評価」に分けて読みつつ、仕組みと直し方を、CVE 公式記録と LocalStack の実装で裏を取って整理します。未認証のローカルサービスが入口になる構図はIDOR とは|認可チェックの抜けを塞ぐ設計、AWS を扱うツールの権限の既定値の落とし穴はAWS API MCP サーバの fail-open(CVE-2026-16584)、依存するツールの欠陥が開発環境を崩す全体像はサプライチェーン攻撃とは|手口・防御ツール比較・npm 12の新機構まで実践解説にまとめています。

WHAT HAPPENED時系列

LocalStack の deploy GUI の XSSと、分類の割れ

  1. 発見

    deploy ページで反射型 XSS

    CloudFormation の deploy GUI が templateURL を未エスケープで埋め込む。4.9.2 以前が対象

  2. CVE-2025-65200

    当初は「任意コード実行」で登録

    MITRE の記述は templateURL 経由のコード実行。だが分類に異議が出た

  3. CISA 再分類

    XSS(CWE-79)・CVSS 9.6 に修正

    実体は未認証オリジン上の反射型 XSS。同一オリジンで Lambda を作成・実行できるため高スコア

  4. 修正

    PR #13293 で deploy GUI を削除(v4.9.3)

    機能自体を撤去。関連 CVE-2025-69723 は反射型 XSS(CVSS 6.1)として別登録

出典: CVE-2025-65200 / 69723 公式記録、CISA vulnrichment、localstack の deploy_ui.py とタグ別存在(2026-10-29 確認)

30秒でわかる

  • ・何が起きたか:deploy GUI の反射型 XSS。templateURL が未エスケープで埋め込まれ、細工リンクで JS が動く
  • ・なぜ重いか:未認証オリジン(port 4566)で動き、同一オリジンから Lambda を作成・実行→コンテナ内でコード実行
  • ・分類の割れ:CVE-2025-65200 は CISA が XSS(CWE-79)・CVSS 9.6 に再分類。関連 XSS の CVE-2025-69723 は 6.1
  • ・対応:4.9.3 以上へ(deploy GUI 撤去)。port 4566 を信頼できない相手に晒さない

まず自分の版を確かめます。4.9.3 未満なら上げます。

localstack --version

これは侵害の判定ではなく、deploy GUI が撤去された版かの確認です。

何が起きるのか

LocalStack は、AWS の各サービスを手元で模してくれる開発ツールで、既定で port 4566 に API を公開します。問題は、CloudFormation 用の deploy GUI(_localstack/cloudformation/deploy)にありました。このページは、URL の templateURL パラメータの値をエスケープせずにそのまま HTML へ埋め込みます。そのため、templateURL に JavaScript を仕込んだ URL を開発者に開かせると、そのページの文脈でスクリプトが動きます。これが反射型 XSS です。

LocalStack は、AWS を実際に使わずに Lambda や S3、CloudFormation などを手元やCIで試せるため、開発やテストで広く使われています。今回の deploy GUI は、CloudFormation のテンプレートを画面から流し込むための補助機能でした。便利さと引き換えに、テンプレートの場所を URL パラメータで受け取る入口が、そのまま反射の口になっていた、という形です。修正でこの GUI ごと撤去されたのは、機能の利用者が限られていた(非推奨・非機能とされた)ことも背景にあります。

反射型 XSS は、URL などに入れた値が、そのままページに跳ね返って(reflect)スクリプトとして動く攻撃です。多くの XSS は「ブラウザ上で他人になりすます」程度で収まりますが、今回が重いのは、この deploy ページが LocalStack の API と同じオリジン(port 4566・未認証)で動き、ページに AWS SDK まで読み込まれている点です。

XSS が、なぜコード実行になるのか

THE CHAIN連鎖の流れ

仕込んだリンクが、コンテナ内で動くまで

  1. 仕込んだリンク

    templateURL を含む URL を開発者に開かせる

  2. 未エスケープで反射

    deploy ページが templateURL の値をそのまま HTML に埋め込む(反射型 XSS)

  3. 未認証オリジンで JS 実行

    ページは port 4566 の API と同一オリジン。AWS SDK も読み込まれている

  4. Lambda を作成・実行

    注入 JS が LocalStack の API を叩き、Lambda を作って呼ぶ

  5. コンテナ内でコード実行

    Lambda は Docker コンテナ内で動くため、そこでコードが走る

出典: CISA vulnrichment の説明と localstack の deploy ページ実装(2026-10-29 確認・PoC は非掲載)

鍵は、どこで JavaScript が動くかです。注入されたスクリプトは、LocalStack の API と同一オリジンで実行されます。LocalStack の API は既定で認証を要求しないため、同一オリジンのスクリプトはそのまま API を叩けます。deploy ページには AWS SDK も読み込まれているので、注入されたスクリプトは Lambda 関数を作成して呼び出すところまで行けます。Lambda は LocalStack の Docker コンテナの中で実行されるため、結果としてコンテナ内で任意のコードが走ることになります。「ブラウザの XSS」が「コンテナ内のコード実行」へ橋渡しされる、という連鎖です。ここでは攻撃に使える具体的な手順(PoC)は示しません。

根にあるのは、特定の1行のバグというより設計上の前提です。ローカル開発ツールは「手元だから信頼できる」として、API に認証を求めず、管理用の画面を同じオリジンに同居させがちです。その前提の上に未エスケープの反射が1つ乗ると、ブラウザで動くだけだったはずの XSS が、その場の API を自由に叩ける足場に変わります。LocalStack が修正を「入力のエスケープ」ではなく「deploy GUI の撤去」で行ったのは、この同居そのものを無くす判断で、根を断つという意味では理にかなっています。

確定していること、評価が割れていること

この件は、CVE の分類そのものが議論になりました。読み方を整理します。

区分 内容
確定 deploy GUI が templateURL を未エスケープで反射する(実装で確認)。未認証オリジン(port 4566)で動き、AWS SDK を読み込む
確定 修正は PR #13293 で deploy GUI(deploy_ui.py)を撤去。4.9.2 に存在し、4.9.3 以降は削除されている
評価が割れる CVE-2025-65200 は当初「任意コード実行」。CISA が反射型 XSS(CWE-79)・CVSS 9.6 に再分類。関連 XSS の CVE-2025-69723 は CVSS 6.1
読み方 入口は同じ deploy ページの XSS。どこまでの影響(ブラウザ内か、Lambda 経由のコード実行か)を測るかでスコアが変わる

CVE の記述やスコアは、後から直されることがあります。この件も、最初の「任意コード実行」という記述に対し、CISA が「実体は XSS だが、未認証オリジンゆえに影響はコード実行まで及ぶ」として CWE と CVSS を付け直しました。分類名(XSS かコード実行か)だけで深刻度を判断せず、どのオリジンで・何ができるかという成立条件まで見るのが、過不足のない評価につながります。同じ入口でも、ブラウザ内で止まるのか、認証なしの API を経てコード実行まで届くのかで、取るべき優先度は大きく変わります。CVE の番号やスコアは出発点として使い、最終的には自分の構成でどこまで到達されうるかで判断するのが実務的です。

何を直すか

対応は、LocalStack を 4.9.3 以上へ上げることが中心です。4.9.3 では deploy GUI 自体が撤去されているので、このページ経由の経路はそのまま塞がれます。あわせて、ネットワーク面の基本も見直します。

区分 やること
更新 LocalStack を 4.9.3 以上へ(PR #13293 で deploy GUI 撤去)。deploy ページが無くなる
露出 port 4566 を信頼できないネットワークや他人のブラウザから触れる状態にしない。localhost 束縛を基本に
運用 _localstack/cloudformation/deploy へ誘導するリンクは敵対的なものとして扱う。開発環境でも最小権限・最新版を保つ

自分の環境を確認する

版の確認に加えて、deploy ページが残っていないかを、動いている LocalStack に対して見ます。4.9.3 以降では、このエンドポイントは無くなっています。

curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4566/_localstack/cloudformation/deploy

200 など「ページが返る」なら deploy GUI が残る古い版、404 なら撤去済みの版です。これは「いま攻撃されているか」ではなく、修正の入った版かの確認です。確認できたら、4.9.3 以上へ上げ、port 4566 の到達範囲を見直します。

影響範囲

直接の対象は、deploy GUI を持つ版(4.9.2 以前)の LocalStack を動かし、その port 4566 を信頼できない相手から触れる状態にしている環境です。典型的には、開発者の端末や CI で、ポートを広く開けている、あるいは同じ端末で開いた悪意あるページから localhost:4566 を叩かれる、といった経路が考えられます。後者は見落とされがちです。LocalStack を起動したまま普段どおりブラウザで別のサイトを見ているとき、そのサイトに仕込まれたリンクやスクリプトが localhost:4566 の deploy ページへ誘導すれば、ポートを外部に公開していなくても、同じ端末のブラウザが橋渡しをしてしまいます。「外から 4566 を塞いだから安全」とは限らず、開発者が開くリンクやページ自体が入口になりうる、という視点が要ります。だからこそ、版を上げて deploy GUI を無くすことが、設定の工夫よりも確実な対処になります。LocalStack はあくまでローカル開発用ですが、だからこそ「どうせローカルだから」と露出や更新を後回しにしがちで、そこが狙われます。版を 4.9.3 以上へ上げ、API ポートの到達範囲を絞ること、そして開発環境のツールも本番と同じ基準で最新に保つことが、確実な備えになります。

参照ソース