CSS Injection
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
CSS Injection
Selettore di Attributo
I selettori CSS sono progettati per corrispondere ai valori degli attributi name
e value
di un elemento input
. Se l'attributo value dell'elemento input inizia con un carattere specifico, viene caricato una risorsa esterna predefinita:
Tuttavia, questo approccio presenta una limitazione quando si tratta di elementi di input nascosti (type="hidden"
) perché gli elementi nascosti non caricano gli sfondi.
Bypass per Elementi Nascosti
Per aggirare questa limitazione, puoi mirare a un elemento fratello successivo utilizzando il combinatore di fratelli generali ~
. La regola CSS si applica quindi a tutti i fratelli che seguono l'elemento di input nascosto, causando il caricamento dell'immagine di sfondo:
Un esempio pratico di sfruttamento di questa tecnica è dettagliato nel frammento di codice fornito. Puoi visualizzarlo qui.
Requisiti per l'iniezione CSS
Affinché la tecnica di iniezione CSS sia efficace, devono essere soddisfatte determinate condizioni:
Lunghezza del Payload: Il vettore di iniezione CSS deve supportare payload sufficientemente lunghi per ospitare i selettori creati.
Rivalutazione CSS: Dovresti avere la possibilità di incorniciare la pagina, il che è necessario per attivare la rivalutazione del CSS con payload generati di recente.
Risorse Esterne: La tecnica presuppone la possibilità di utilizzare immagini ospitate esternamente. Questo potrebbe essere limitato dalla Content Security Policy (CSP) del sito.
Selettore di Attributo Cieco
Come spiegato in questo post, è possibile combinare i selettori :has
e :not
per identificare contenuti anche da elementi ciechi. Questo è molto utile quando non hai idea di cosa ci sia all'interno della pagina web che carica l'iniezione CSS.
È anche possibile utilizzare quei selettori per estrarre informazioni da diversi blocchi dello stesso tipo come in:
Combinando questo con la seguente tecnica @import, è possibile esfiltrare molte info utilizzando l'iniezione CSS da pagine cieche con blind-css-exfiltration.
@import
La tecnica precedente ha alcuni svantaggi, controlla i prerequisiti. Devi essere in grado di inviare più link alla vittima, oppure devi essere in grado di iframe la pagina vulnerabile all'iniezione CSS.
Tuttavia, c'è un'altra tecnica ingegnosa che utilizza CSS @import
per migliorare la qualità della tecnica.
Questo è stato mostrato per la prima volta da Pepe Vila e funziona in questo modo:
Invece di caricare la stessa pagina più e più volte con decine di payload diversi ogni volta (come nella precedente), caricheremo la pagina solo una volta e solo con un import al server dell'attaccante (questo è il payload da inviare alla vittima):
L'importazione andrà a ricevere alcuni script CSS dagli attaccanti e il browser li caricherà.
La prima parte dello script CSS che l'attaccante invierà è un altro
@import
al server degli attaccanti di nuovo.Il server degli attaccanti non risponderà a questa richiesta ancora, poiché vogliamo leakare alcuni caratteri e poi rispondere a questo import con il payload per leakare i successivi.
La seconda e più grande parte del payload sarà un payload di leak dell'attributo selettore
Questo invierà al server degli attaccanti il primo carattere del segreto e l'ultimo
Una volta che il server degli attaccanti ha ricevuto il primo e l'ultimo carattere del segreto, risponderà all'import richiesto nel passo 2.
La risposta sarà esattamente la stessa dei passi 2, 3 e 4, ma questa volta cercherà di trovare il secondo carattere del segreto e poi il penultimo.
L'attaccante seguirà quel ciclo fino a riuscire a leakare completamente il segreto.
Puoi trovare il codice originale di Pepe Vila per sfruttare questo qui o puoi trovare quasi lo stesso codice ma commentato qui.
Lo script cercherà di scoprire 2 caratteri ogni volta (dall'inizio e dalla fine) perché il selettore di attributo consente di fare cose come:
Questo consente allo script di rivelare il segreto più velocemente.
A volte lo script non rileva correttamente che il prefisso + suffisso scoperto è già la flag completa e continuerà in avanti (nel prefisso) e indietro (nel suffisso) e a un certo punto si bloccherà. Nessun problema, controlla semplicemente l'output perché puoi vedere la flag lì.
Altri selettori
Altri modi per accedere a parti del DOM con CSS selectors:
.class-to-search:nth-child(2)
: Questo cercherà il secondo elemento con classe "class-to-search" nel DOM.:empty
selettore: Usato ad esempio in questo writeup:
XS-Search basato su errore
Riferimento: Attacco basato su CSS: Abusare di unicode-range di @font-face , XS-Search basato su errore PoC di @terjanq
L'intenzione generale è di utilizzare un font personalizzato da un endpoint controllato e garantire che il testo (in questo caso, 'A') venga visualizzato con questo font solo se la risorsa specificata (favicon.ico
) non può essere caricata.
Utilizzo di Font Personalizzati:
Un font personalizzato è definito utilizzando la regola
@font-face
all'interno di un tag<style>
nella sezione<head>
.Il font è chiamato
poc
ed è recuperato da un endpoint esterno (http://attacker.com/?leak
).La proprietà
unicode-range
è impostata suU+0041
, mirata al carattere Unicode specifico 'A'.
Elemento Object con Testo di Riserva:
Un elemento
<object>
conid="poc0"
è creato nella sezione<body>
. Questo elemento cerca di caricare una risorsa dahttp://192.168.0.1/favicon.ico
.La
font-family
per questo elemento è impostata su'poc'
, come definito nella sezione<style>
.Se la risorsa (
favicon.ico
) non riesce a caricarsi, il contenuto di riserva (la lettera 'A') all'interno del tag<object>
viene visualizzato.Il contenuto di riserva ('A') verrà reso utilizzando il font personalizzato
poc
se la risorsa esterna non può essere caricata.
Stile Scroll-to-Text Fragment
La :target
pseudo-classe è impiegata per selezionare un elemento mirato da un frammento URL, come specificato nella specifica CSS Selectors Level 4. È fondamentale comprendere che ::target-text
non corrisponde a nessun elemento a meno che il testo non sia esplicitamente mirato dal frammento.
Un problema di sicurezza sorge quando gli attaccanti sfruttano la funzionalità Scroll-to-text fragment, consentendo loro di confermare la presenza di testo specifico su una pagina web caricando una risorsa dal loro server tramite iniezione HTML. Il metodo prevede l'iniezione di una regola CSS come questa:
In questi scenari, se il testo "Administrator" è presente sulla pagina, la risorsa target.png
viene richiesta dal server, indicando la presenza del testo. Un'istanza di questo attacco può essere eseguita tramite un URL appositamente creato che incorpora il CSS iniettato insieme a un frammento Scroll-to-text:
Qui, l'attacco manipola l'iniezione HTML per trasmettere il codice CSS, mirando al testo specifico "Administrator" attraverso il frammento Scroll-to-text (#:~:text=Administrator
). Se il testo viene trovato, la risorsa indicata viene caricata, segnalando involontariamente la sua presenza all'attaccante.
Per la mitigazione, si devono notare i seguenti punti:
Corrispondenza STTF Constrainata: Il frammento Scroll-to-text (STTF) è progettato per corrispondere solo a parole o frasi, limitando così la sua capacità di rivelare segreti o token arbitrari.
Restrizione ai Contesti di Navigazione di Livello Superiore: Lo STTF opera esclusivamente nei contesti di navigazione di livello superiore e non funziona all'interno di iframe, rendendo qualsiasi tentativo di sfruttamento più evidente per l'utente.
Necessità di Attivazione da Parte dell'Utente: Lo STTF richiede un gesto di attivazione da parte dell'utente per funzionare, il che significa che gli sfruttamenti sono fattibili solo attraverso navigazioni avviate dall'utente. Questo requisito riduce notevolmente il rischio che gli attacchi vengano automatizzati senza interazione dell'utente. Tuttavia, l'autore del post del blog sottolinea condizioni specifiche e bypass (ad es., ingegneria sociale, interazione con estensioni di browser prevalenti) che potrebbero facilitare l'automazione dell'attacco.
Essere consapevoli di questi meccanismi e delle potenziali vulnerabilità è fondamentale per mantenere la sicurezza web e proteggere contro tali tattiche sfruttative.
Per ulteriori informazioni, controlla il rapporto originale: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/
Puoi controllare un exploit che utilizza questa tecnica per un CTF qui.
@font-face / unicode-range
Puoi specificare font esterni per valori unicode specifici che saranno raccolti solo se quei valori unicode sono presenti nella pagina. Ad esempio:
Quando accedi a questa pagina, Chrome e Firefox recuperano "?A" e "?B" perché il nodo di testo di sensitive-information contiene i caratteri "A" e "B". Ma Chrome e Firefox non recuperano "?C" perché non contiene "C". Questo significa che siamo stati in grado di leggere "A" e "B".
Estrazione del nodo di testo (I): legature
Riferimento: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację
La tecnica descritta implica l'estrazione di testo da un nodo sfruttando le legature dei caratteri e monitorando i cambiamenti di larghezza. Il processo prevede diversi passaggi:
Creazione di font personalizzati:
I font SVG sono creati con glifi che hanno un attributo
horiz-adv-x
, che imposta una grande larghezza per un glifo che rappresenta una sequenza di due caratteri.Esempio di glifo SVG:
<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>
, dove "XY" denota una sequenza di due caratteri.Questi font vengono quindi convertiti in formato woff utilizzando fontforge.
Rilevamento dei cambiamenti di larghezza:
Viene utilizzato CSS per garantire che il testo non vada a capo (
white-space: nowrap
) e per personalizzare lo stile della barra di scorrimento.L'apparizione di una barra di scorrimento orizzontale, stilizzata in modo distintivo, funge da indicatore (oracolo) che una specifica legatura, e quindi una specifica sequenza di caratteri, è presente nel testo.
Il CSS coinvolto:
Processo di sfruttamento:
Passo 1: Vengono creati font per coppie di caratteri con larghezza sostanziale.
Passo 2: Viene impiegato un trucco basato sulla barra di scorrimento per rilevare quando il glifo di grande larghezza (legatura per una coppia di caratteri) viene reso, indicando la presenza della sequenza di caratteri.
Passo 3: Una volta rilevata una legatura, vengono generati nuovi glifi che rappresentano sequenze di tre caratteri, incorporando la coppia rilevata e aggiungendo un carattere precedente o successivo.
Passo 4: Viene effettuata la rilevazione della legatura di tre caratteri.
Passo 5: Il processo si ripete, rivelando progressivamente l'intero testo.
Ottimizzazione:
Il metodo di inizializzazione attuale che utilizza
<meta refresh=...
non è ottimale.Un approccio più efficiente potrebbe coinvolgere il trucco CSS
@import
, migliorando le prestazioni dello sfruttamento.
Estrazione del nodo di testo (II): leaking the charset with a default font (not requiring external assets)
Riferimento: PoC using Comic Sans by @Cgvwzq & @Terjanq
Questo trucco è stato rilasciato in questo thread di Slackers. Il charset utilizzato in un nodo di testo può essere leakato utilizzando i font predefiniti installati nel browser: non sono necessari font esterni -o personalizzati-.
Il concetto ruota attorno all'utilizzo di un'animazione per espandere progressivamente la larghezza di un div
, consentendo a un carattere alla volta di passare dalla parte 'suffisso' del testo alla parte 'prefisso'. Questo processo divide efficacemente il testo in due sezioni:
Prefisso: La riga iniziale.
Suffisso: La/e riga/e successiva/e.
Le fasi di transizione dei caratteri apparirebbero come segue:
C ADB
CA DB
CAD B
CADB
Durante questa transizione, viene impiegato il trucco unicode-range per identificare ogni nuovo carattere man mano che si unisce al prefisso. Questo viene realizzato cambiando il font in Comic Sans, che è notevolmente più alto del font predefinito, attivando di conseguenza una barra di scorrimento verticale. L'apparizione di questa barra di scorrimento rivela indirettamente la presenza di un nuovo carattere nel prefisso.
Sebbene questo metodo consenta di rilevare caratteri unici man mano che appaiono, non specifica quale carattere è ripetuto, solo che è avvenuta una ripetizione.
Fondamentalmente, il unicode-range viene utilizzato per rilevare un carattere, ma poiché non vogliamo caricare un font esterno, dobbiamo trovare un altro modo. Quando il carattere è trovato, gli viene assegnato il font Comic Sans preinstallato, che rende il carattere più grande e attiva una barra di scorrimento che leakerà il carattere trovato.
Controlla il codice estratto dal PoC:
Text node exfiltration (III): leaking the charset with a default font by hiding elements (not requiring external assets)
Reference: This is mentioned as an unsuccessful solution in this writeup
Questo caso è molto simile al precedente, tuttavia, in questo caso l'obiettivo di rendere specifici char più grandi di altri è nascondere qualcosa come un pulsante da non premere da parte del bot o un'immagine che non verrà caricata. Quindi potremmo misurare l'azione (o la mancanza di azione) e sapere se un char specifico è presente all'interno del testo.
Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets)
Reference: This is mentioned as an unsuccessful solution in this writeup
In questo caso, potremmo provare a leakare se un char è nel testo caricando un font falso dalla stessa origine:
Se c'è una corrispondenza, il font verrà caricato da /static/bootstrap.min.css?q=1
. Anche se non verrà caricato con successo, il browser dovrebbe memorizzarlo nella cache, e anche se non c'è cache, c'è un meccanismo di 304 not modified, quindi la risposta dovrebbe essere più veloce rispetto ad altre cose.
Tuttavia, se la differenza di tempo della risposta memorizzata nella cache rispetto a quella non memorizzata non è abbastanza grande, questo non sarà utile. Ad esempio, l'autore ha menzionato: Tuttavia, dopo aver testato, ho scoperto che il primo problema è che la velocità non è molto diversa, e il secondo problema è che il bot utilizza il flag disk-cache-size=1
, il che è davvero pensato.
Estrazione del nodo di testo (III): perdita del charset misurando il caricamento di centinaia di "font" locali (senza richiedere risorse esterne)
Riferimento: Questo è menzionato come una soluzione non riuscita in questo writeup
In questo caso puoi indicare CSS per caricare centinaia di font falsi dalla stessa origine quando si verifica una corrispondenza. In questo modo puoi misurare il tempo necessario e scoprire se un carattere appare o meno con qualcosa come:
E il codice del bot appare così:
Quindi, se il font non corrisponde, il tempo di risposta quando si visita il bot dovrebbe essere di circa 30 secondi. Tuttavia, se c'è una corrispondenza del font, verranno inviati più richieste per recuperare il font, causando un'attività continua nella rete. Di conseguenza, ci vorrà più tempo per soddisfare la condizione di stop e ricevere la risposta. Pertanto, il tempo di risposta può essere utilizzato come indicatore per determinare se c'è una corrispondenza del font.
Riferimenti
Impara e pratica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Last updated