今年見つけたFirefoxの脆弱性 (2020年)
これは Recruit Engineers Advent Calendar 2020 の 22日目の記事です。
この記事では、私が今年報告したFirefoxブラウザの脆弱性の中から、主観的に発生原理が面白いと思うものを3点ほど紹介します。
見つけた脆弱性の一覧は以下のとおりです。これらはすでに修正済みであり、開発元のMozillaから情報公開の許可を得ています。透明性のある対応に感謝します。
この記事で触れていない脆弱性のうちいくつかは、MozillaのAttack & Deffenceというセキュリティブログに寄稿しておりますので、宜しければそちらもご覧ください。それでは、はじめていきましょう。
1. XSSによるサーバ証明書検証エラーのバイパス
まずは、お馴染みのXSSです。本来、XSSはWebアプリの脆弱性ですが、Webブラウザの中にも存在することがあります。たとえば、サーバに接続できない場合に表示されるエラー画面を思い浮かべてください。あれはサーバから取得したHTMLではなく、Webブラウザの内部のリソースです。こうしたWebブラウザ内部のHTMLページに、XSSの脆弱性が作り込まれてしまうことがあるのです。
私がXSS(Bug 1657055)を見つけたのは、Firefox for Androidのサーバ証明書エラー画面でした。今年の夏、Firefox for Androidは全面的にアーキテクチャを変更しており、この過程で、エラー画面のHTMLに初歩的なXSSを作り込んでしまったのです。
以下がその脆弱性です。アドレスバーのクエリ文字列に含まれる<s>という文字がHTMLとしてページに展開され、画面上のテキストに取り消し線が入っていることがわかります。
この画面の下の方には「Accept the Risk and Continue」と書かれたボタンがあります。不正なサーバと接続してしまうリスクを理解した上で、それでもなお、ページを開くかどうかを確認するためのボタンです。
このボタンを押した際の処理は、JavaScriptのacceptAndContinueという関数で定義されています。これはつまり、先ほどのXSSを用いてacceptAndContinue関数を実行させることができれば、ユーザの意思とは関係なく、証明書に不備のあるページを開かせることができるというわけです。
この攻撃手法を実証したのが以下の画像です。XSSを引き起こす文字列をURLのクエリ文字列に付けることで、本来は証明書のエラーでアクセスできないはずのページ(https://self-signed.badssl.com)を表示させることに成功しています。
同様の文字列をURLに付加することで、サーバ証明書に不備のある様々なサイトにアクセスさせることが可能でした。Mozillaからはこの脆弱性の報酬として、$5,000を頂きました。
2. リモートデバッグポートへの不正接続
次に、リモートデバッグ機能の脆弱性(Bug 1658865, CVE-2020–26964)です。リモートデバッグ機能は開発者機能(DevTools)の一部で、その名の通り、Desktop版のFirefoxから、USBで接続しているAndroid端末上のFirefoxのデバッグを可能とするものです。
このリモートデバッグ機能は、以下のような仕組みで実現されています。ポイントは、Android端末上のFirefoxは、UNIXドメインソケットで通信を待ち受けており、そのソケットをPCのTCPポートにフォワードすることで、Desktop版のFirefoxからの制御を可能にしているという点です。
このリモートデバッグプロトコル自体には認証機能がありません。しかし、UNIXドメインソケットへの接続がAndroid端末のSELinuxポリシーで制限されているため、他のアプリがリモートデバッグ機能を悪用することはできないようになっています。
私が報告した脆弱性は、このUNIXドメインソケットの接続制限を迂回できるというものです。UNIXドメインソケットの利用がAndroidで禁止されたのは、Android 6(Marshmallow)以降であり、Android 5.1(Lollipop)以前の端末であれば、他のアプリがリモートデバッグプロトコルを操作し、Firefoxで開かれている任意のページの情報を盗み出せてしまうのです。
Android 5.1は6年以上前にリリースされたOSですが、報告時点では約8%の市場シェアを有しており、Firefox for Androidのサポート対象でもありました。このため、本脆弱性の対策として、Android 6未満の端末ではリモートデバッグ機能を有効化できないようにする処理が追加されました。
3. WKWebViewのスクリプト注入タイミングの不備
最後に紹介するのはFirefox for iOSの脆弱性ですが、その詳細に入る前に攻撃の原理を説明します。
iOSアプリでは、WebKit以外の方法でWebページを表示することがAppleの規約で禁止されています。この規約はMozillaやGoogleのようなブラウザベンダーに対しても同様です。このため、iOS版のFirefoxやChromeは、自社のブラウザエンジンの代わりに、Appleが提供するWKWebViewを利用しています。
しかし、WKWebViewを利用していては、各ブラウザの独自性を出すことができません。そこで各ブラウザベンダーは、WKUserScriptという仕組みを用いて、ブラウザ独自の機能をJavaScriptコードとして注入することで、WKWebViewをカスタマイズしています。
WKUserScriptを注入するタイミングには2種類あります。WKWebViewがページをロードした直後(atDocumentStart)と、ロード完了後(atDocumentEnd)です。
ここで注意すべきは、atDocumentEndのスクリプトが注入される前に、現在開いているWebページ自体のスクリプトが実行されるという点です。このため、もし仮に悪意のあるページが、atDocumentEndで注入されるものと同じ名前のオブジェクトをImmutableで定義した場合、atDocumentEndでのスクリプトは注入されず、結果として、ブラウザはWKWebViewのカスタマイズに失敗します。
この原理を攻撃に転じることができたのが、以下2つの脆弱性です。
3–1. ログイン管理機能の改ざんによるパスワードの窃取
Firefox for iOSは、JavaScriptのログインハンドラをページに埋め込むことで、認証画面のオートコンプリートを実現しています。
そこで、このログインハンドラのJavaScriptを以下のように改ざんすることで、本来はHTMLのフォームに自動入力されるべきパスワードを外部へ盗み出すことができました(Bug 1654131, CVE-2020–15661)。
この脆弱性を悪用するには、あらかじめ、攻撃者が不正なJavaScriptをページへ仕込んでおく必要があります。しかし、この脆弱性はログイン画面だけでなく、サイト上の全てのページで悪用可能であるため、仮にサイトのどこかにXSSの脆弱性が1つでもあれば、ユーザのパスワードが盗み出せる被害につながる恐れがありました。
3–2. ダウンロード管理機能によるクロスオリジンの情報窃取
同様の手法で、ダウンロード管理機能のハンドラを改ざんすることで、他のサイト上でダウンロードされたコンテンツのURLを盗み出すことができました(Bug 1653827, CVE-2020–15662)。
以下の例では、悪意のあるサイト(csrf.jp)上のiframeに、攻撃対象のサイト(cern.ch)がロードされています。このiframe内でダウンロードされたコンテンツのURLは、本来、クロスオリジンの関係である親のウィンドウに漏洩してはいけませんが、親ウィンドウのダウンロード管理機能のハンドラを改ざんすることで、サブフレームの情報が盗み出せるというものでした。
これらの脆弱性はいずれも、JavaScriptコードを注入するタイミングをatDocumentStartに変えることで修正されました。さらに、iOS14で追加されたWKContentWorldを用いることにより、Webページのスクリプトとの間で、JavaScriptのオブジェクト名が衝突しないようにする保護策が導入されました。
最後に
この記事では、今年報告したFirefoxの脆弱性の中から個人的に原理の面白いと思うものを3つ紹介しました。来年も新しい技術に触れ、ワクワクする日々を過ごせることを願っています。それでは、Happy Holidays, and Happy Hacking!