Disclaimer: The techniques described in this document are intended solely for ethical use and educational purposes. Unauthorized use of these methods outside approved environments is strictly prohibited, as it is illegal, unethical, and may lead to severe consequences.
It is crucial to act responsibly, comply with all applicable laws, and adhere to established ethical guidelines. Any activity that exploits security vulnerabilities or compromises the safety, privacy, or integrity of others is strictly forbidden.
Table of Contents
Summary of the Vulnerability
In this lab, the core weakness lies in how the web cache and browser handle URLs differently. The application contains an XSS vulnerability, but browsers automatically apply URL-encoding, which prevents the payload from executing directly. However, the cache performs normalization of the URL before storing and serving it.
By exploiting this difference, an attacker can craft a malicious request that appears harmless to the browser but is stored in a normalized form by the cache. When a victim later visits the poisoned URL, the cache delivers a version of the page containing the attacker's injected payload. This payload executes arbitrary JavaScript — demonstrated in the lab by triggering alert(1)
.
Steps to Reproduce & Proof of Concept (PoC)
① Open the Lab
Start by accessing the provided PortSwigger lab environment. At first glance, the lab appears to have a comment form, which seems like a common place to test for XSS injection.
② Initial Payload Attempt
I tried a straightforward payload inside the comment field:
'"><img/src=x onerror=alert(1)>

However, nothing was triggered.

③ Research on Cache Normalization
I reviewed the PortSwigger documentation on normalized cache keys: Normalized cache keys — PortSwigger.

This reminded me that caches often normalize requests before serving them, which could be leveraged to bypass the encoding barrier.
④ Encoding the Payload
Using CyberChef, I encoded the payload:
%27%22%3E%3Cscript%3Ealert(1)%3C/script%3E

This gave me a URL-encoded form of the attack string.
⑤ First Request Attempt
I tried placing the encoded payload into the postId
parameter like this:
GET /post?postId=3%27%22%3E%3Cscript%3Ealert(1)%3C/script%3E HTTP/2
The server responded with: "Invalid blog post ID"
No execution occurred, but the response header included X-Cache: miss
, which hinted that normalization could play a role.
⑥ Refining the Injection
Since injecting directly into postId
didn't work, I removed the parameter and directly appended the payload to the path.

Server sent me a different response, stated 404 Not Found with encoded value, so I trying to send the raw (decoded) payload:
GET /post/3'"><script>alert(1)</script> HTTP/2
The server reflected the payload back in the response body. At first, it was still served as X-Cache: miss
.

⑦ Cache Hit and Delivery
By repeatedly sending the same request, eventually the cache served the response with:
X-Cache: hit
This meant the poisoned version of the page was stored in the cache.
I then copied the malicious URL and used the Deliver exploit to victim feature:
https://<your-lab-id>.web-security-academy.net/post/3'"><script>alert(1)</script>


When the victim accessed the link, the payload executed successfully, solving the lab.

Impact
This makes URL normalization flaws particularly dangerous, as they exploit the "gaps" between different layers of web infrastructure. In real-world environments, this could mean widespread compromise of users, reputational damage to the target organization, and potential compliance violations.
📢 Enjoyed this post? Stay connected! If you found this article helpful or insightful, consider following me for more:
- 📖 Medium: bashoverflow.medium.com
- 🐦 Twitter / X: @_havij
- </> Github: havij13
🙏Your support is appreciated.