CSRF: A complete guide to exploiting advanced CSRF vulnerabilities
By novasecio
August 19, 2024
Cross-site request forgery—or for short CSRF—vulnerabilities are one of the most exploited web security vulnerabilities that result in performing unwanted actions. This client-side vulnerability can sometimes go unnoticed but delivers a devastating impact depending on the context. From basic action modification (such as liking a post, following a new member or updating a metric's value) to a full account takeover by changing the victim's email address or password, disabling 2-FA or resetting a password.
In this article, we will go over how you can identify CSRF vulnerabilities while also covering basic and advanced exploitation methods. Let's first start with defining what CSRF vulnerabilities are.
What are CSRF vulnerabilities?
Cross-Site Request Forgery (CSRF) vulnerabilities arise when a malicious actor can trick the victim's browser into performing any unauthorized action on the victim's behalf.
As usual, depending on the context, this can lead to a variety of outcomes. From basic object modification (such as adding, liking or deleting a post) to more severe actions like resetting a password, changing the account's email address or disabling any security measures like 2-FA.
Prefer a video instead? Watch our video about CSRF vulnerabilities!
Identifying CSRF Vulnerabilities
Cross-Site Request Forgery (CSRF) vulnerabilities in the most basic form arise when a malicious actor can trick the victim's browser into performing an action. However, not every action (API endpoint or method) is vulnerable. Certain conditions have to be met before we can consider a specific endpoint or app route to be vulnerable to CSRF. Let's take a look at it in detail.
Browser security measures
CSRF vulnerabilities are not a new vulnerability class yet can still have a devastating impact on a vulnerable web app. Web browsers have tried to mitigate them as much as possible throughout the years by implementing a series of security measures such as more strict cookie policies.
When is it vulnerable to CSRF?
For an app route or API endpoint to be vulnerable and exploitable to CSRF, the following 3 main conditions have to be met:
The targeted functionality or feature must be privileged
Your session ID must be a cookie with the SameSite cookie policy set to "None" or "Lax"
And lastly, the HTTP request shouldn't carry any unpredictable values
Let's elaborate more on these 3 conditions.
1) The targeted functionality or feature must be privileged
The endpoint or app route must perform a privileged action. In general, it should bring change to data on the victim's behalf. This can be adding, updating or deleting data for example.
You will often notice that no measures have been taken to protect public contact forms. However, exploiting CSRF in this context will usually result in no impact.
2) Your session ID must be a cookie with the SameSite cookie policy set to "None" or "Lax"
The session ID cookie must have the SameSite policy set to "None" or "Lax". When it's set to anything else other than "None" or "Lax", the browser will refuse to forward the cookies during exploitation. That will make the CSRF attempt futile as the server won't be receiving any authentication credentials, a crucial step in CSRF exploitation.
This also means that endpoints that require a non-standard HTTP header or Authorization request header for authentication are not susceptible to CSRF attacks as credentials won't be automatically forwarded.
There is, however, an exception to this rule. If your target makes use of HTTP Basic or a certificate, your browser will forward your credentials in each request.
3) The HTTP request shouldn't carry any unpredictable values
When the request contains an unpredictable value or token, it will make any of our CSRF attempts futile as the same unpredictable token will also be required in our CSRF proof of concept.
Developers often take advantage of that and introduce an "anti-CSRF" token in each state-changing HTTP request. However, sometimes the token doesn't get correctly validated or is not a random token.
With all this in mind, let's take a look at some exploitation examples.
1) Exploiting basic CSRFs
Consider the following example HTTP request:
Basic CSRF example
CSRF in the most basic form can be exploited by crafting the following proof of concept:
<!DOCTYPE html>
<html>
<body>
<h1>CSRF Proof of Concept</h1>
<form method="POST" action="https://app.example.com/api/profile/update">
<input type="hidden" name="new_email" value="user@example.com">
<input type="submit" value="Submit Request">
</form>
</body>
</html>
The proof of concept can be hosted and the link to that resource is then sent to the victim. When opened, the following steps describe a CSRF attack:
The victim will open the malicious link sent by the attacker
The victim's browser will perform the HTTP request
The server will acknowledge the HTTP request and make the state-changing action on behalf of the victim
Sometimes it can be that easy, but usually it's not. Let's take a look at some other more advanced cases.
2) Content-Type based CSRF
In some contexts, developers develop web apps to only send data over to the API in JSON. Some content types, like JSON, trigger another browser security measure (CORS) and will make our CSRF attempt futile.
To bypass this case, we can check if the API server is capable of handling other content types such as form data. Oftentimes, this is the case as the underlying code or external package is still configured and capable of parsing other content types.
CSRF Example 2
You may also come across an API that only accepts data as JSON. Even in that case, it is possible to send data in JSON without triggering CORS by enforcing the "text/plain" content type!
CSRF Example 3
<!DOCTYPE html>
<html>
<body>
<form action="https://app.example.com/api/profile/update" method="POST" enctype="text/plain">
<input type="hidden" name='{"test":"x' value='y","new_email":"attacker@example.com"}'/>
<input type="submit" value="Submit request"/>
</form>
<script>history.pushState('','','/');document.forms[0].submit();</script>
</body>
</html>
3) Method-based CSRF
Another CSRF case is method-based CSRF. This case can be exploited by simply changing the HTTP method. HTTP methods like PUT or PATCH trigger CORS as well.
To bypass this, we can try to change the HTTP method (and content type if necessary) to POST or PUT.
CSRF Example 4
4) No validation of anti-CSRF tokens
As mentioned earlier, some developers resort to using anti-CSRF tokens. Anti-CSRF (also commonly referred to as anti-XSRF tokens) are randomly generated values that are sent with requests. This makes it more complex for an attacker to craft a proof of concept as the token is never guessable. But that's unfortunately not always the case.
If validation is not correctly performed, we can try to modify the token and check if the API accepts our request:
without the anti-CSRF parameter
with a blank value as the anti-CSRF token
with another random value
with another random value of the same length as the original anti-CSRF token
TIP! Cross-check if your anti-CSRF token is tied to your session. If this is not the case, it is still possible to bypass anti-CSRF protection by just hardcoding a valid anti-CSRF token in your proof of concept!
5) Referrer-based CSRF
The Referer request header is often processed by analytics tools but is sometimes also used to prevent CSRF. The approach developers commonly take when using the referer header as an anti-CSRF mitigation is by validating it (or part of it). If the validated value is matched against a whitelist, the request is accepted.
CSRF Example 5
We can bypass this whitelist if no strict patterns have been defined:
CSRF Example 6
Another case you should test for is when no referer header is sent with the request and when there is no fallback set. Developers think that it is not possible to send a request through a browser without a referer header. We can however craft a proof of concept that'd do just that, send a request without a referer header and bypass this anti-CSRF case:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Prevent referer header from being sent -->
<meta name="referrer" content="no-referrer">
</head>
<body>
<form action="https://app.example.com/api/profile/update" method="POST">
<input type="hidden" name="new_email" value="attacker@example.com"/>
<input type="submit" value="Submit request"/>
</form>
<script>history.pushState('','','/');document.forms[0].submit();</script>
</body>
</html>
Automated tooling
Testing all API endpoints and app routes for CSRF attacks can be a tedious task, especially in a complex web application. Automated tooling can help ease out this task, and often with quite high accuracy as CSRF attacks are simple.
Below are a few open-source tools listed that can help with automating CSRF attacks.
Bolt
Bolt is an automated CSRF exploitation tool developed in Python3. Bolt is equipped with a crawling engine and is capable of crawling your target.
https://github.com/s0md3v/Bolt
XSRFProbe
XSRFProbe is an advanced CSRF exploitation toolkit. It is equipped with a crawling engine and is capable of performing extensive CSRF tests.
https://github.com/0xInfection/XSRFProbe
CSRF PoC Creator
CSRF PoC Creator is a Burpsuite extension that can help you generate proof of concepts from raw HTTP requests in Burpsuite. It is available for both the Community as well as the Professional edition of Burpsuite.
https://github.com/rammarj/csrf-poc-creator
Time to practice!
Let's take a look at the code snippets below, can you spot which one is vulnerable to CSRF?
What code snippet is vulnerable to CSRF?
Share your answer with us on Twitter/X (we promise to make sure to respond to you!)
Conclusion
CSRF vulnerabilities are often easy to spot but finding a state-changing method that carries a lot of impact often proves to be more difficult. In this article, we went over some of the most common exploitation cases as well as some more advanced ones.
So, you’ve just learned something new about CSRF vulnerabilities… Right now, it’s time to put your skills to the test! You can start by practicing on vulnerable labs or... browse through our 70+ public bug bounty programs on Intigriti and who knows, maybe earn a bounty on your next submission!