0.0.0.0 Day:ブラウザからローカルホストAPIを悪用する

概要

Oligo Securityのリサーチチームは最近、「0.0.0.0 Day」脆弱性を公表しました。 この脆弱性により、悪意のあるWebサイトがブラウザのセキュリティを回避し、組織のローカルネットワーク上で稼働するサービスとやり取りできるようになり、ネットワーク外部の攻撃者によるローカルサービスへの不正アクセスやリモートコード実行につながる可能性があります。

この問題は、ブラウザごとにセキュリティ機構の実装が一貫していないこと、そしてブラウザ業界における標準化の欠如に起因します。その結果、一見無害に見えるIPアドレス0.0.0.0が、開発用途、OS、さらには内部ネットワークで使われるローカルサービスを攻撃者が悪用するための強力な手段となり得ます。  

0.0.0.0 Dayの影響は広範で、個人にも組織にも及びます。ShadowRayのような実際の悪用キャンペーンが確認されたことは、この脆弱性への対処が急務であることをさらに強調しています。

エグゼクティブサマリー

要点(TL;DR) 

Oligoのリサーチチームは最近、主要なWebブラウザすべてに影響する重大な脆弱性を発見しました。これにより攻撃者はローカルネットワークへ侵入できるようになります。私たちが「0.0.0.0 Day」と名付けたこの発見は、ブラウザがネットワークリクエストを扱う方法に根本的な欠陥があることを示しており、悪意ある行為者がローカルデバイス上で稼働する機微なサービスへアクセスできてしまう可能性があります。

イントロ

Oligo Securityの研究者は、主要ブラウザ(Chromium、Firefox、Safari)すべてに存在する論理的な脆弱性を公表しました。これにより外部Webサイトが、MacOSおよびLinux上でローカルに動作するソフトウェアと通信(および潜在的に悪用)できるようになります。Windowsはこの問題の影響を受けません。

Oligoの研究者は、(.comで終わるドメインのような)公開Webサイトが、localhost/127.0.0.1の代わりに0.0.0.0というアドレスを使うことで、ローカルネットワーク(localhost)上で稼働するサービスと通信し、訪問者のホスト上で任意のコードを実行できる可能性があることを突き止めました。 

0.0.0.0 Day – 深掘り

はじめに

ブラウザ——誰しもお気に入りがあり、毎日使っています。ブラウザ以外のアプリケーションでも、Google Analyticsや同様のクライアントサイドSDKを使ったり、スクリプトや動画を埋め込んだりする際に、外部ドメインからリソースを読み込むことがよくあります。

ブラウザは常に攻撃対象であり、ブラウザ開発者はサンドボックス化やHTTPS-ONLYクッキーといった画期的なセキュリティ概念を導入したり、クロスサイトリクエストを巡る標準(CORS:Cross Origin Resource Sharing)を実装してサーバーとエンドユーザーを保護してきました。これらはすべて、悪意のあるWebサイトがクロスサイトリクエストフォージェリ(CSRF)攻撃を用いてユーザーの個人データ、内部ネットワーク、ローカルアプリケーションに近づくのを防ぎます。

ブラウザは設計上、Javascriptを使ってほぼあらゆるHTTPサーバーへリクエストを送信できます。クロスサイトのレスポンスを扱う際、ブラウザのセキュリティ機構はどの動作を取るかを決定します:

  • 有効: レスポンスデータをJavascriptコンテキストへ伝播させる(成功)
  • 無効:マスクされたレスポンスを返す、または特別なエラーを発生させる(CORS、SOP、…)。

しかし、場合によってはレスポンスがまったく重要ではありません。
0.0.0.0 Day脆弱性では、たった1回のリクエストだけで被害が発生し得ます。詳細に入る前に、理解しておくべき背景があります。  

最も異例なIP:そもそも0.0.0.0とは?

問題の始まりに戻りましょう。0.0.0.0には「複数の用途」があります。
すでにいくつか思い浮かぶかもしれません:「このホスト上のすべてのIP」「このホスト上のすべてのネットワークインターフェース」あるいは単に「localhost」。
RFC 1122
では、0.0.0.0を{0,0}という表記で参照しています:

IPv4において宛先アドレスとして0.0.0.0を使用することを禁じ、DHCPハンドシェイク中のDHCPDISCOVERパケットで初めてIPが割り当てられる場合など、特定の状況下でのみ送信元アドレスとして許可しています。0.0.0.0は、特定ドメインをブロックするため(広告ブロックとして)/etc/hostsファイルで使われることもあります。またネットワークポリシーでCIDRブロック0.0.0.0/32を用いる場合、「すべてのIPが許可される」という意味になります。

なぜこのWebサイトは私をポートスキャンしているのか?

Webサイトのユーザーをデジタルに「フィンガープリント」することは、さまざまな目的を持つ既知の手法です。 最も一般的な正当用途はリピーターの識別ですが、脅威アクターがフィッシングキャンペーンのために情報収集する目的でも使われます。ユーザーに関する追加データと突き合わせることで、Webサイトは「今訪問しているのが誰か」を多く推測できます——たとえ一度もログインしていなくても。
2020年5月、Hacker Newsに興味深い見出しが掲載されました:

Hacker Newsに興味深い見出しが掲載された

このケースでは、EbayがWebサイトの読み込み直後に訪問者をポートスキャンしようとしたようです。この手法により、WebサイトはJavascriptを使ってlocalhost(127.0.0.1)のポートをスキャンし、興味深いユニークなフィンガープリントを得ていました。

Ebayが使用したJavascriptコードは、有効な応答(そのポートで何かが動いている)とHTTPエラー(そのポートで何も待ち受けていない)を区別できた。

本来、ブラウザはそもそもこれらのリクエストを送信できるべきではありません。なぜなら、たった1回のリクエストが悪用につながり得るからです(このブログでこれから示します)。これは長年インターネットがそう動いてきたというだけで、誰も気にしていませんでした。この挙動が侵害につながり得ると完全に理解されるまでには時間がかかり——気づいた頃には、すべてのブラウザの一部になっていて、修正が非常に困難になっていました。

18年前のバグ? 

ローカルおよび内部サービスは、常に攻撃者の主要ターゲットでした。
特に興味深いセキュリティ問題がMozillaに報告されており、2006年——Chromeが2008年に初リリースされる前——まで遡ります:

18年前のバグ報告。今も未解決(Open)。

このバグ報告では、あるユーザーが公開Webサイトが内部ネットワーク上のルーターを攻撃したと主張し、Webサイトはそのようなことができるべきではないと考えていました。 
当時、内部ネットワーク(そしてインターネット全体)は設計上安全ではありませんでした。多くのサービスには認証がなく、SSL証明書やHTTPSも至る所に存在していたわけではありません。Webサイトは安全でないHTTPで読み込まれ、攻撃者は悪意の目的で常にブラウザを出し抜いていました。

2006年以降、数多くの攻撃キャンペーンが「ブラウザはレスポンスに注目する一方で、リクエストは依然として送出される」という事実を悪用してきました。たとえば攻撃者が管理するWebサイトに悪意あるJavascriptを仕込むことで、家庭やオフィスのルーター設定を変更できてしまいます。

18年が経ち、何百ものコメントが付いたにもかかわらず、このバグは今日に至るまで未解決のままです。
この18年の間に、この問題はクローズされたり、再オープンされたり、「重大」や「クリティカル」へ優先度が変更されたり、さらには実際に悪用されたりもしました。

メンテナはバグの性質について合意するのに苦労しました:

  • これは「脆弱性」なのか? 
  • Firefox固有なのか?
  • 機能改善の要望なのか? 

Firefoxの一部メンテナは、これはバグでも機能でもないと主張しました。バグ報告はクローズされ、再オープンされ、優先度が付け直され——そしてFirefoxがPNAを実装するまで、今後もオープンのままとなるでしょう。
このバグは、たった1回のHTTPリクエストで発火し得ました——レスポンスは重要ではありません。悪意あるscriptタグの例は2006年時点ですでに文書化されており、実際に家庭用ルーターを標的にした事例もありました:

標準化の欠如こそが、この苦痛の主因でした——そのため、すべてのブラウザにおけるベースラインのセキュリティ機構を開発する明確な必要性が生まれました。世界は、主要ブラウザすべてでCORSを拡張し、ローカル・プライベート・パブリックネットワークを区別できる標準化を切望していました。

GoogleはPrivate Network Accessでこのギャップに大胆に踏み込みました。 

PNA(Private Network Access)とは?

長い間、ブラウザが「よりプライベートでないコンテキスト」からローカルまたは内部ネットワークへリクエストを送る際に、どう振る舞うべきかは明確ではありませんでした。attacker.comのようなドメインがlocalhostへ接続できるべきではありません——現実世界のどんなシナリオでも。

主要ブラウザはCORSに依存してきました。CORSは大いに役立ちますが、その効果はレスポンス内容に依存するため、リクエスト自体は依然として作成・送信され得ます。これは単純に不十分です。歴史が示すとおり、たった1回のHTTPリクエストで家庭用ルーターを攻撃できるのです——それだけで成立するなら、すべてのユーザーは「そもそもそのリクエストが発生しないようにする」手段を持つ必要があります。

幸いにも、ChromeはPNA(Private Network Access)を導入しました:

この新しい標準は、プライベートネットワーク上のサーバーへWebサイトがリクエストを送る能力を制限することでCORSを拡張します。
PNAは、パブリック・プライベート・ローカルネットワークを区別することを提案します。より安全でないコンテキストで読み込まれたページは、より安全なコンテキストと通信できません。たとえばattacker.comは、よりプライベートと見なされる127.0.0.1や192.168.1.1に接続できません。

出典:https://developer.chrome.com/blog/private-network-access-update

PNAはCORSとは異なります。CORSは、安全でないコンテキストで意図しないコンテンツが読み込まれるのを防ぐだけで、レスポンスレベルで保護します。レスポンスで使われるリソースは マスクされるか破棄されます。PNAはこれを強化し、リクエスト自体をそもそも送信させない能力を導入します。

0.0.0.0を検証する:PNAバイパス

現在のPNA仕様によれば、次のIPセグメントはプライベートまたはローカルと見なされます:

私たちの調査では、「0.0.0.0」がこのリストに含まれていないことに気づきました。PNAの一部として、Webサイトは0.0.0.0へリクエストを送出できないはずだと考えました。仕様上、ターゲットとして使用すべきではないからです。
確かめるため、localhost(127.0.0.1)上でダミーのHTTPサーバーを動かしました。
次に、外部ドメインからJavascriptで0.0.0.0を使ってアクセスを試みました。

……単純に動作しました。リクエストはサーバーに到達しました。 

ここで何が起きたのか?
1. 公開ドメイン(.com)配下で、ブラウザは0.0.0.0へリクエストを送信した。
2. ダミーサーバーは127.0.0.1で待ち受けている(ループバックインターフェースのみで待ち受けており、すべてのネットワークインターフェースではない)。
3. localhost上のサーバーがリクエストを受け取り、処理し、レスポンスを返す。
4. ブラウザはCORSによりレスポンス内容がJavascriptへ伝播するのをブロックする。

つまり、公開Webサイトはレスポンスを見られないままでも、ホスト上で開いている任意のポートへアクセスできるということです。

私たちはこれが現行PNA実装のバイパスであり、ブラウザに内在する欠陥だと理解しました。責任ある開示に従って、発見内容をすべてのブラウザに報告しましたが、主張を裏付けるためには現実的な脅威と実際の攻撃ベクトルが必要でした。 

脆弱なローカルアプリケーションを見つける

まず、問題になり得るアプリケーションを見つける必要がありました——そして選択肢は豊富でした。多くのアプリケーションが0.0.0.0 Day脆弱性の影響を受ける可能性があります。 

サービスがlocalhostを使うとき、制約された環境を前提にします。この前提は(この脆弱性のケースのように)誤っている場合があり、その結果として安全でないサーバー実装につながります。たとえば、多くのアプリケーションはCSRFトークンのチャレンジを省略し、厳密に制御されたネットワーク環境で動くはずだという理由で、認可や認証を妥協します。

場合によっては、認可や認証が不要だったり、CSRFトークンの検証が存在しなかったりします。アプリが安全な環境、あるいは信頼された隔離ネットワークで動作している兆候を見せると、認可やCSRFトークンを欠いたPOSTのHTTPルートや、リソース・設定への書き込みアクセスを許可し——コード実行を可能にします。たった1回のHTTPリクエストでも、ポートへのアクセスを許してしまうことがあります。

ブラウザから脆弱になり得るローカルアプリケーションを見つけるため、まずローカルポート(localhostネットワークインターフェース)で動作するHTTPサーバーが必要でした。
さらに、リモートコード実行を得てこの脆弱性を完全に悪用するには、ファイルや設定を書き込み・調整・変更できるHTTPルートが必要でした。ここでも選択肢は豊富でした。現実のアプリケーションには多くのエンドポイントがあり、ローカルサービスはこうしたセキュリティ上の妥協を実際に行っているのです——攻撃者にとっては朗報です。 

ほどなくして、最初の脆弱なアプリケーションが見つかりました:Rayです。

POC:ブラウザからのShadowRay

AIワークロードを標的とする最近の攻撃キャンペーンShadowRayは、Oligoによって発見され、実際に野放しで悪用されていました。私たちの研究者は今回、0.0.0.0を攻撃ベクトルとして用いることで、この攻撃をブラウザから実行可能であることを証明しました。

ShadowRayは、意図せずインターネットに公開された場合に任意コード実行を可能にし、約1年近く発見されませんでした。私たちはRayの大ファンで、開発のためにローカルでよく使っていました。そこで自問しました:「公開Webサイトが、localhost上で動作するRayクラスターを悪用できるだろうか?」

Image

説明:まず右側のターミナルで、localhost上にローカルRayクラスターを起動します。次に左側のターミナルで、新規接続を待ち受けるソケットを起動し、リバースシェルを開けるようにします。その後、被害者がメール内のリンクをクリックすると、エクスプロイトが実行されます。エクスプロイトは訪問者のマシン上で攻撃者向けのリバースシェルを開きます。

以下はエクスプロイトに使用したサンプルコードです

Image

Chromium

Image

Safari

Image

Firefox

またしても……単純に動作しました。

あまりにも簡単でした。ブラウザからShadowRayを実行できるということは、この手法で可能になるリモートコード実行攻撃が間違いなく膨大に存在することを意味します——そこで私たちは、さらに探すことにしました。

Selenium Grid

SeleniumGreedのような最近の攻撃キャンペーンでは、脅威アクターがSelenium Gridの公開サーバーを悪用し、既知のリモートコード実行脆弱性を使って組織への初期アクセスを獲得していることが示されました。
ローカルのSelenium Gridインスタンスでは、細工したペイロードを伴うPOSTリクエストをhttp://0.0.0.0:4444/へ送出することでRCEが可能であることを確認しました。

もう一つの興味深い攻撃ベクトル:ローカルのSelenium Gridクラスターを使って、安全でないブラウザ設定のままWebサイトを閲覧させ、VPNの背後にある内部ドメインやプライベートDNSレコードへアクセスする。

Pytorch Torchserve(ShellTorch)

2023年7月、Oligo Research Teamは、CVSS 9.8のCVE-2023-43654を含む複数の新たな重大脆弱性を、PytorchのメンテナであるAmazonおよびMetaに開示しました。これらの脆弱性は総称してShellTorchと呼ばれ、PyTorch TorchServeにおけるリモートコード実行(RCE)につながり、最終的に攻撃者がサーバーへ完全かつ不正にアクセスできるようになります。

内部ネットワークでTorchServeを使用するAI実務者(ローカル利用やポートフォワーディングを含む)の場合、これらの脆弱性は0.0.0.0経由でも悪用可能であり、ファイアウォールやWAFの背後にあるローカルTorchServeクラスターの侵害につながります。

開放ポートに基づいて再訪ユーザーを特定する

もう一つの興味深い攻撃ベクトルは、ポートスキャンによって匿名ユーザー——特にクッキーがなく、一度もログインしたことがないユーザー——を識別できる能力です。ローカルポートスキャンの結果は、User-Agent、IPアドレス、その他の識別子(https://amiunique.org/が強調しているような)と突き合わせることができます。以下のポートは、組織内の異なるペルソナによって使用されます。

結論:あなたのlocalhostはどれほど「ローカル」か?

PNAは素晴らしい——Googleとコミュニティが主導する、本当に驚くべき取り組みです。しかしPNAが完全に展開されるまでの間、公開WebサイトはJavascriptを使ってHTTPリクエストを送出し、ローカルネットワーク上のサービスへ到達できてしまいます。 これを変えるには、PNAの標準化と、その標準に従ったブラウザ実装が必要です。

CORSもまた素晴らしく、すでにインターネットをはるかに安全にしています。CORSはレスポンスが攻撃者に届くのを防ぐため、攻撃者は無効なリクエストを送ってもデータを読み取れません。
リクエスト送信時、レスポンスにCORSヘッダーが存在しなければ、攻撃者のJavascriptコードはレスポンス内容を読み取れません。

CORSはJavaScriptへ伝播する前にレスポンスを止めるだけですが、不透明(opaque)なリクエストは「no-cors」モードで送出でき、レスポンスを気にしないのであればサーバーへ正常に到達してしまいます。 

私たちのデモでは、0.0.0.0と「no-cors」モードを組み合わせることで、攻撃者が公開ドメインを使ってlocalhost上で稼働するサービスを攻撃し、たった1回のHTTPリクエストだけで任意コード実行(RCE)さえ得られることを証明しました。

私たちの報告により、ブラウザは修正を優先し、破壊的変更を行って、ターゲットIPとしての0.0.0.0をブロックしました。ブラウザが修正を導入することで互いに「ゼロデイ化」してしまう状況を避けるため、協調的な修正が重要でした。

0.0.0.0-Dayからローカルアプリケーションを守るには? 

当然ながら、ブラウザの修正を待つのは理想的ではありません——そのため、開発者がローカルアプリケーションを守るためにできることがあります。

主なポイントは次のとおりです:

  1. PNAヘッダーを実装する
  2. DNSリバインディング攻撃(localhostや127.0.0.1への)を防ぐため、リクエストのHOSTヘッダーを検証する。
  3. localhostネットワークが「ローカル」だからといって信用しない——localhostで動かしている場合でも、最小限の認可レイヤーを追加する。Jupyter Notebook の開発者はこれをうまく行い、デフォルトでトークンを追加しました。
  4. 可能であればHTTPSを使用する。
  5. ローカルであっても、アプリケーションにCSRFトークンを実装する。
  6. ブラウザはゲートウェイとして振る舞い、多くのブラウザで内部IPアドレス空間へのルーティング能力を持つことを忘れない。

専門家のヒント

Avi Lumelsky

AIセキュリティ研究者

Avi Lumelskyは、エンジニアリングとAIを専門とするセキュリティ研究者です。Oligo Securityでは、オープンソースプロジェクトの脆弱性を発見することでAIインフラを保護しています。以前はDeci AI(現在はNVIDIAの一部)に在籍し、モデル最適化に注力していました。彼の研究はGoogleやMetaなどの大手企業向けのレポートにつながり、ForbesやHacker Newsでも取り上げられています。また、オープンソースのeBPFプロジェクトを保守し、AIフレームワークや推論サーバーの脆弱性も探究しています。

購読して最新のセキュリティ更新情報を受け取る

最新アプリもレガシーアプリも防御するために設計

Oligoは、K8s上に構築された最新のクラウドアプリや、オンプレミスでホストされる旧来のアプリ向けに、数分で導入できます。

翻訳元: https://www.oligo.security/blog/0-0-0-0-day-exploiting-localhost-apis-from-the-browser

ソース: oligo.security