Saturday, November 16, 2024

Mozilla Firefox 0-day: URLプロトコルハンドラの漏洩 [CVE-2024-9398, CVE-2024-5690]

 筆者:satoki

はじめに

はじめまして。リチェルカセキュリティのSatoki Tsujiです。業務ではWeb脆弱性診断やWebの新規攻撃手法の研究を行っています。

本記事では、AVTOKYO2024にて発表した「Mozilla FirefoxのInformation disclosureの0-day脆弱性(CVE-2024-9398, CVE-2024-5690)」について解説します。本記事で解説する脆弱性によって、本来ブラウザにより秘匿されるべきURLプロトコルハンドラの設定の有無がページ経由で漏洩します。結果として、攻撃者はターゲットユーザマシンにインストールされている様々なアプリケーションを特定できます。

Mozilla Firefox 0-day: Browser Side-Channel Attack to Leak Installed Applications

注意
本脆弱性の解説はMozillaより許可を得て公開しています。本記事は、セキュリティ研究と教育を目的としており、記事内の情報を不適切な形で利用した如何なる損害についても、責任を負いません。

 

URLプロトコルハンドラとは

URLプロトコルハンドラとは、ブラウザやOSが特定のプロトコルスキーム(例えば、http,ftp,mailto)に基づいて、URLをどのように処理するかを定義する仕組みです。URLにアクセスした際に、どのようなアプリケーションを起動するかを決定するために使用されます。具体例としては、mailto:satoki@example.comのようなリンクをクリックした際にデフォルトのメールクライアントが開くのは、URLプロトコルハンドラがmailtoスキームのURLを取り扱うように設定されているためです。

一般的なスキームはIANAに登録されており、その他にもアプリケーションが独自に設定したカスタムスキームも存在します。以下にスキームの例を示します。

スキーム 機能の概要
http WebページへのアクセスにHTTPプロトコルを使用する
javascript Webページ内でJavaScriptを実行する
file ファイルシステムにアクセスする
steam Steamプラットフォーム内で特定の機能を呼び出す
zoommtg Zoomで会議を開始または会議に参加する
example スキームの例示に用いる

スキームを用いたアプリケーションの起動時には、ユーザへ確認を促すダイアログが表示されます。以下にsteamスキームを開いた際の例を示します。

アプリケーションはURLプロトコルハンドラへ独自のカスタムスキームを設定できます。詳細は述べませんが、WindowsではレジストリにURL Protocolキーと実行ファイルのパスおよび実行パラメータを登録することで、カスタムスキームが利用可能となります。

 

URLプロトコルハンドラの漏洩リスク

URLプロトコルハンドラの設定の有無が攻撃者に漏洩した場合に、どのようなリスクがあるでしょうか。FortiGuard Labs Threat Research Reportでは、漏洩した場合の影響を以下のようにまとめています。

Identifying communication channels: By listing the handlers an attacker can get a hint to what platforms he may use for reaching the targeted user. For instance, detecting social applications such as Slack, Skype, WhatsApp or Telegram may be used for communicating with the target.

General reconnaissance: A wide range of applications nowadays uses custom URL handlers and can be detected using this vulnerability. Some examples: music players, IDE, office applications, crypto-mining, browsers, mail applications, antivirus, video conferencing, virtualizations, database clients, version control clients, chat clients, voice conference apps, shared storages

Pre-exploitation detection: Exploit kits may leverage this information in order to identify if a potentially vulnerable application is present without exposing the vulnerability itself.

Detecting Security solutions: Many security solutions such as AV products register protocol handlers whose presence can be exposed by leveraging the vulnerabilities because they have custom protocol handlers installed. Attackers may use this to further customize their attack to be able to circumvent any protection mechanism set by those security solutions.

User Fingerprinting: reading what protocol handlers exist on a system may also be used in order to improve browser/user fingerprinting algorithms.

URLプロトコルハンドラの設定の有無が漏洩した場合、攻撃者が存在するハンドラを列挙できます。結果として、ターゲットユーザのマシンにインストールされた様々なアプリケーションを確認することができます。具体的には、slackスキームが存在すればSlackがインストールされており、skypeスキームが存在すればSkypeがインストールされていると分かります。

特定のアプリケーションのインストールの有無が露見した場合にどのようなリスクがあるでしょうか。ターゲットユーザが日常的に使用しているコミュニケーションツールが判明した場合には、攻撃者がアカウントを調査し接触を図る手掛かりになります。インストールされているアプリの種類から、ターゲットの属性を推測できます。金融系のアプリが分かればフィッシングの精度向上にも寄与します。ターゲットをWebサイト内でトラッキングする際のフィンガープリンティングにも使用できます。

ターゲットユーザが導入しているセキュリティソフトを特定し、検知回避など次の攻撃の足掛かりとする可能性もあります。以下にFortiGuard LabsのRotem Kerner氏の報告にある、攻撃者にとって有用なカスタムスキームの例を引用します。2020年の報告であるため、現在のサポート状況は不明です。

スキーム ベンダ名
GDataGDATAToastNews GData
malwarebytes MalwareBytes
avastpam Avast
vizorwebs, tmtb, titanium, vizorweb TrendMicro
bdlaunch BitDefender


imgタグのサイズをオラクルとした既知の脆弱性

Mozilla Firefox 82未満では、Rotem Kerner氏よりURLプロトコルハンドラの漏洩の脆弱性が報告されています。この脆弱性は、CVE-2020-15680として採番されており、Mozilla Foundation Security Advisoryに記載されたImpactはmoderateとされます。

CVE-2020-15680: Presence of external protocol handlers could be determined through image tags

彼はimgタグのsrcにカスタムスキームを指定した場合の挙動差を発見しました。以下のようなimgタグを二つ用意します。

<img src="ms-settings://satoki">
<img src="satoki://satoki">

ms-settingsスキームにはWindows設定アプリが起動するよう設定されており、satokiスキームには何も設定されていません。開発者ツールを開き、各imgタグのサイズを確認します。

ms-settings://satokiのimgタグ

satoki://satokiのimgタグ

どちらも画像としての読み込みが失敗しますが、スタイルのサイズが異なっていることが分かります。Firefoxは画像の正常な読み込みに失敗した場合に、壊れた画像を示すアイコンを表示します。このアイコンのサイズが24×24です。ms-settingsスキームではアイコンが表示されています。一方、satokiスキームのようにハンドラが設定されていないスキームではアイコンが表示されないため、サイズは0×0となります。

このサイズの差をオラクル(未知のものを推測する手掛かりとなる既知の情報)とすることで、攻撃者はURLプロトコルハンドラの設定の有無を判別することができます。JavaScriptで大量のimgタグを生成し、各スタイルのwidthが24と一致するか検証することで、サイトの運営者は訪問者のコンピュータにどのようなURLプロトコルハンドラが設定されているかを知ることができます。

 

window.openのエラーをオラクルとした手法 (CVE-2024-9398)

今回、著者が発見した脆弱性の1件目です。本脆弱性では、window.openの戻り値へのアクセス時に発生するエラーの有無をオラクルとして、URLプロトコルハンドラの設定の有無を取得できます。ターゲットユーザがポップアップを許可する必要があるため、危険性は低くなっています。

CVE-2024-9398: External protocol handlers could be enumerated via popups

脆弱性調査を行う中でwindow.openでカスタムスキームを開いた際に、異なるページ表示が行われることを発見しました。開発者ツールのコンソールを開き、以下のJavaScriptを実行します。

open01 = window.open("ms-settings://satoki");
open02 = window.open("satoki://satoki");

ポップアップを許可すると、以下のような二つのページが新しくオープンされます。

ms-settings://satokiで開かれるページ

satoki://satokiで開かれるページ

ms-settingsスキームではアプリケーションを開くため、ユーザへ確認を促すダイアログが表示されています。一方、satokiスキームのようにハンドラが設定されていないスキームでは、ページ読み込みエラーが表示されています。これは開くアプリケーションが存在しないために起こります。この差を知ることはできないでしょうか。

window.openを実行した開発者ツールのコンソールに戻って、返り値のdocumentオブジェクトにアクセスしてみましょう。 

ページ読み込みエラーとなったページのdocumentオブジェクトへのアクセスが、拒否されてエラーとなっています。この挙動はopen02[0]のような配列アクセスでも同様となります。このエラーをキャッチすることで、オラクルとして用いることができます。他にも以下のようにframesの差を用いたオラクルも可能です。

ポップアップが許可されているという条件のもとですが、攻撃者はwindow.openを用いてカスタムスキームを大量に開き、各documentオブジェクトへのアクセスがエラーとなるか検証します。この結果により、攻撃者はURLプロトコルハンドラの設定の有無を判別できます。不自然に開いたページは、最後にwindow.closeですべて閉じることで処理できます。

window.historyの変化をオラクルとした手法

先ほどまでのエラーをオラクルとした手法との違いはほとんどありませんが、Historyの興味深いふるまいを利用した手法も発見しています。CTFなどではよく知られている、History Length経由でユーザの情報を取得するテクニックを応用します。本手法はwindow.openのエラーをオラクルとした手法 (CVE-2024-9398)に含めて報告しています。window.openで開くまでは同様となりますが、window.locationを更新した際のwindow.historyの変化を利用します。

ハンドラが設定されているms-settings://satokiをwindow.openした後に、window.locationをフラグメント付きのms-settings://satoki#satokiに変更し、その後に再度window.locationをabout:blankに変更します。するとwindow.history.lengthは1となります。どうやらwindow.locationの更新ではHistoryが増加しないようです。

一方、ハンドラが設定されていないスキームsatoki://satokiをwindow.openし、window.locationをsatoki://satoki#satokiabout:blankと二度変更します。すると、window.history.lengthへアクセスが可能となり、window.history.lengthは2となります。こちらはHistoryが増加するようです。

これらの挙動をまとめると、window.locationの更新ではハンドラが設定されていないスキームのみ、Historyが増加します。このwindow.historyの変化をオラクルとして、攻撃者はURLプロトコルハンドラの設定の有無を判別できます。

iframe.contentWindow.history.lengthのエラーをオラクルとした手法

window.openで新しいページを開くためには、ポップアップの許可が必要となります。ポップアップはターゲットユーザが意図的に許可しなければならず、ユーザのアクションが必要となるためステルス性が低下します。ターゲットユーザがポップアップを許可する必要のない手法も発見しており、window.openのエラーをオラクルとした手法 (CVE-2024-9398)に含めて報告しています。本手法はステルス性を向上させるため、カスタムスキームごとにiframeを作成し、iframe.contentWindow.history.lengthへアクセスした際のエラーの有無を利用します。

ハンドラが設定されているms-settings://satokiとハンドラが設定されていないsatoki://satokiの二つのiframeを作成します。開発者ツールのコンソールを開き、以下のJavaScriptを実行します。注意点として、Firefoxではある程度の間隔をあけなければiframeを連続して開くことができない制約があります。

iframe01 = document.createElement("iframe");
document.body.appendChild(iframe01);
iframe01.sandbox="";
iframe01.src = "ms-settings://satoki";
// sleep 10~20s
iframe02 = document.createElement("iframe");
document.body.appendChild(iframe02);
iframe02.sandbox="";
iframe02.src = "satoki://satoki";

すると以下のような二つのiframeが新しく開かれます。ここではsandbox属性により、ユーザへ確認を促すダイアログを表示させないテクニックも用いています。

ms-settingsスキームのようにハンドラが設定されている場合は空白のページ、satokiスキームのようにハンドラが設定されていない場合はページ読み込みエラーが表示されています。これはwindow.openと同様です。ここで各iframeのcontentWindow.history.lengthへアクセスしてみましょう。

satokiスキームでのcontentWindow.history.lengthのアクセスが、拒否されてエラーとなっています。この挙動はabout:blankでiframeを作成した後に、srcを指定することで発生します。このエラーをキャッチすることで、オラクルとして用いることができます。

攻撃者は指定した間隔ごとにiframeでカスタムスキームを開き、contentWindow.history.lengthへのアクセスがエラーとなるか検証します。結果により、攻撃者はURLプロトコルハンドラの設定の有無を判別できます。また、ポップアップが一度でも許可されているという条件のもとではiframeを連続して開くことができるため、window.openよりも強力な手法と言えます。

 

imgタグのonerror発火時間をオラクルとした手法 (CVE-2024-5690)

今回、著者が発見した脆弱性の2件目です。本脆弱性では、imgタグの生成から読み込みエラーイベント(onerror)が発火するまでの時間をオラクルとして、URLプロトコルハンドラの設定の有無を取得できます。ターゲットユーザのアクションは不要です。

CVE-2024-5690: External protocol handlers leaked by timing attack

脆弱性調査を行う中で、imgタグにカスタムスキームを設定した際におけるイベントの発火について検証を行いました。イベントは以下のようにimgタグに設定できます。

<img src="ms-settings://satoki" onerror="alert('ms-settings')">
<img src="satoki://satoki" onerror="alert('satoki')">

幸いなことにイベントの発火はハンドラの設定の有無とは無関係に行われることが分かりました。そこで、イベントが発火するまでの時間を計測してみることにしました。開発者ツールのコンソールを開き、以下のJavaScript関数を作成します。

async function measureLoadTime(ph, numberOfTrials) {
    let totalTime = 0;
    for (let i = 0; i < numberOfTrials; i++) {
        const startTime = performance.now();
        await new Promise(resolve => {
            const img = document.createElement("img");
            document.body.appendChild(img);
            img.onload = img.onerror = function() {
                const endTime = performance.now();
                totalTime += endTime - startTime;
                img.parentNode.removeChild(img);
                resolve();
            };
            img.src = ph;
        });
    }
    return totalTime;
}

この関数では、初めにimgタグを生成します。次に、受け渡された第一引数をimgタグのsrcに設定します。その後に、onloadイベント(読み込み完了)またはonerrorイベント(読み込み失敗)が発火するまでの時間を計測します。さらに、この一連の計測を第二引数の回数だけ繰り返し実行し、累積した時間を返します。つまりmeasureLoadTime("satoki://satoki", 10000);を呼び出すと、satoki://satokiのonerrorイベントが発火するまでの時間を10000回分計測し、累積した時間を返します。この関数を用いて以下のJavaScriptを実行し、カスタムスキームに対し10000回分の時間を計測します。

measureLoadTime("ms-settings://satoki", 10000).then(time => console.log(time));
measureLoadTime("satoki://satoki", 10000).then(time => console.log(time));

順番を入れ替え、複数回行った結果は以下の通りとなりました。

ms-settingsスキームのイベントが発火するまでの時間に比べ、satokiスキームのイベントの発火が倍ほど早いことが分かります。様々なカスタムスキームで検証した結果、ハンドラが設定されているスキームはイベントの発火が遅延していることが判明しました。この遅延をオラクルとして用いることができます。

攻撃者はJavaScriptで大量のimgタグを生成し、各タグのイベント発火までにかかった時間を計測します。速度は環境により異なりますが、ハンドラが設定されていないスキームの値をあらかじめ保持しておき、比較することでURLプロトコルハンドラの設定の有無を判別できます。

 

CSPのreport-uriディレクティブリクエストをオラクルとした手法 (CVE-2024-5690:DUPLICATE)

今回、著者が発見した脆弱性の3件目です。imgタグのonerror発火時間をオラクルとした手法 (CVE-2024-5690)よりも前に報告していましたが、修正が同様の箇所で済んだためDUPLICATEとなりました。タイミングを調整して上手く報告していれば認定されたと感じています。本脆弱性では、imgタグの読み込みをCSP(Content Security Policy)でブロックした際の振る舞いを利用します。CSPのreport-uriディレクティブに設定したURLへのリクエストをオラクルとして、URLプロトコルハンドラの設定の有無を取得できます。ターゲットユーザのアクションは不要です。

脆弱性調査を行う中で、imgタグをCSPでブロックした際の挙動について検証を行いました。CSPをimg-src 'self'に設定したページにおいて、ブロックしたimgタグのスタイルに差が生じればそれを利用することができます。カスタムスキームはselfでないため、すべてが一律でブロックされると予想されます。以下のようなHTMLを作成して調査します。

<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="img-src 'self';">
    </head>
    <body>
        <img src="ms-settings://satoki">
        <img src="satoki://satoki">
    </body>
</html>

開発者ツールでimgタグのスタイルなどを調査していると、コンソールに以下のような奇妙な表示があることに気付きました。

imgタグが二つ含まれているにもかかわらず、CSPによるブロックがms-settingsスキームのみとなっています。これはimgタグの順番を変更しても同様でした。つまり、ハンドラが設定されているスキームはCSPによりリソースの読み込みがブロックされますが、設定されていないスキームはリソースとしての読み込み自体が発生していないと考えられます。読み込み自体が発生していないため、CSPにはブロックされません。このようなCSPでのブロックの有無を外部から観測できるでしょうか。

ここで、CSPには違反(ブロック)を報告するディレクティブであるreport-uriが設定可能である事を思い出しました。Content-Security-Policy: report-uri http://localhost;のように設定することにより、ページ内のコンテンツでCSP違反が発生した際に、ブラウザがhttp://localhostへ違反内容をJSONでPOSTします。report-uriはmetaタグでは指定できないため、以下のように簡易的なサーバプログラムを用意します。

from flask import Flask, request, make_response

app = Flask(__name__)

@app.route("/")
def index():
    response = make_response(
        f"""
<html>
    <body>
        <img src="ms-settings://satoki">
        <img src="satoki://satoki">
    </body>
</html>
"""
    )
    response.headers["Content-Security-Policy"] = (
        f"img-src 'self'; report-uri http://localhost:5555/recv;"
    )
    return response

@app.route("/recv", methods=["POST"])
def recv():
    print(request.get_data().decode("utf-8"))
    return "OK"

if __name__ == "__main__":
    app.run(debug=False, host="0.0.0.0", port=5555)

この簡易的なサーバプログラムはポート5555でアクセスを待ち受け、imgタグを二つ表示します。imgタグにはCSPでimg-src 'self';という制限がかかっています。また、CSPの違反の報告先はreport-uri http://localhost:5555/recv;と指定されています。報告を受け取るパスの/recvでは、受け取ったCSP違反の報告をprintしています。このサーバのページを開いたときにprintされたJSONは以下の通りでした。

{
  "csp-report": {
    "blocked-uri": "ms-settings",
    "column-number": 1,
    "disposition": "enforce",
    "document-uri": "http://localhost:5555/",
    "effective-directive": "img-src",
    "original-policy": "img-src 'self'; report-uri http://localhost:5555/recv",
    "referrer": "",
    "status-code": 200,
    "violated-directive": "img-src"
  }
}

blocked-uriにブロックされたms-settingsスキームが表示されていることが分かります。このリクエストをオラクルとして用いることができます。同様にscriptタグをscript-srcディレクティブで制限した場合や、audioまたはvideoタグをmedia-srcディレクティブで制限した際のリクエストもオラクルとして用いることができます。

攻撃者はあらかじめimgタグを大量にしたページを用意します。ページのCSPはimgタグを必ずブロックするものとし、report-uriディレクティブに攻撃者自身のサーバを設定します。攻撃者は受け取ったCSP違反の報告のblocked-uriの内容から、URLプロトコルハンドラの設定の有無を判別できます。

 

修正

CVE-2024-9398

プロトコルハンドラの設定が有る場合にはabout:blankでポップアップが表示されており、プロトコルハンドラの設定が無い場合にはネットワークエラーページが表示されていました。この差によって、プロパティへのアクセス違反の有無が生じていることが原因でした。プロトコルハンドラの設定が無い場合にもabout:blankを用いることで、差をなくす修正が行われました。

CVE-2024-5690

プロトコルハンドラの設定の有無のチェックが、CSPを含むセキュリティチェックよりも前に行われていたことが原因でした。結果として、エラーイベントが発火するまでの時間にも差が出ています。プロトコルハンドラの設定が無い場合に、早期にリターンを行う機能を削除する修正が行われました。また、DUPLICATEとなった脆弱性も同様の修正で解消されています。

 

おわりに

本記事では、URLプロトコルハンドラの設定の有無がページ経由で漏洩する脆弱性について解説しました。一見無害に思えるURLプロトコルハンドラの設定情報でも、攻撃者視点では悪用手法が見いだせる可能性があります。ブラウザの最適化など実装の違いによる挙動の差はしばしば発生します。セキュリティエンジニアは常にオフェンシブな視点を持ち、無害と思える情報にも疑いの目を向けることが求められます。

リチェルカセキュリティではWeb分野での未知の攻撃を日々研究し、お客様へのソリューションの提供に役立てています。発見した脆弱性の報告はもちろんのこと、登壇資料作成の業務時間への算入や、平日の登壇に対する特別休暇の付与など、外部発表などを支援する登壇支援制度も設けられています。

有名ソフトウェアの0-day脆弱性調査をはじめ、研究開発など若い才能にお任せしたい業務がたくさんあります。この記事を読んで当社の取り組みにご興味を持った方は、ぜひカジュアル面談にお申し込みください。

No comments:

Post a Comment