筆者: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#satoki
、about:blank
と二度変更します。すると、window.history.lengthへアクセスが可能となり、window.history.lengthは2
となります。こちらはHistoryが増加するようです。
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脆弱性調査をはじめ、研究開発など若い才能にお任せしたい業務がたくさんあります。この記事を読んで当社の取り組みにご興味を持った方は、ぜひカジュアル面談にお申し込みください。