Lion Cipher Lab
Poison a web cache with an unkeyed header — then key it, normalize it, and shut the hole
- AppSec / DevSecOps
- Advanced
- ~75 min
- Free · no account required
Why it matters
A single reflected header looks harmless when it only changes the attacker's own page. Put a shared cache in front of that app — a CDN, a reverse proxy, Varnish, an nginx cache — and the same bug becomes a broadcast weapon: the attacker sends ONE crafted request, the cache stores the poisoned response, and then serves it to thousands of ordinary users under the normal URL. That is web cache poisoning, and it turns a low-severity 'reflected' finding into stored XSS, malicious script delivery, or a site-wide denial of service, all without the victims doing anything unusual. The reason it keeps…
What you'll learn
- the keyed-vs-unkeyed input model that makes web cache poisoning possible
- building a caching reverse-proxy + origin lab with Docker to test cache behavior safely
- hunting an unkeyed input with cache busters (without poisoning shared state)
- poisoning a shared cache so a payload is served to other users, proven with X-Cache/Age
- fixing it three ways: stop reflecting untrusted input, add the input to the cache key / Vary, and no-store genuinely dynamic responses
Steps in this lab
- The one idea: keyed vs unkeyed inputs
- Build the target: a caching proxy in front of a trusting origin
- Baseline: watch the cache warm up
- Find the unkeyed input — safely, with a cache buster
- Poison the cache and hit a victim
- Fix #1 — stop reflecting untrusted input (the root cause)
- Fix #2 — when a response MUST vary on an input, put it in the key
- Fix #3 — don't cache what shouldn't be cached
- What a pro does differently — and when this bites hardest
- Clean up