- Weekly Bug Bounty Content
- Posts
- Cross-Site Request Forgery (CSRF): CSRF exploitation
Cross-Site Request Forgery (CSRF): CSRF exploitation
Ultimate Guide into Finding and Exploiting Cross-Site Request Forgery Vulnerabilities
Today's estimated reading time is: 13 min
Last week we saw a brief introduction to cross-site request forgery
In this post, I will show you almost all methods to exploit CSRF vulnerabilities with some common bypasses!
Today's post is brought to you by Nova Security
The cloud-based attack surface management & vulnerability detection platform that finds vulnerabilities before you do.
Waitlist is open, join today ===> https://novasec.io/
Basic CSRF Exploitation:
So we’ve seen what cross-site request forgery issues are
And although it might be still a bit vague for you, today’s post should take care of that as we are going to look more into the exploitation part
Basic CSRF exploitation involves you making a request that causes an action that modifies data on behalf of an authenticated user’s account
And a simple proof of concept may look like this:
https://example.com/messages/compose?receiver=account2%40example.com&message=test&send=1
The above PoC is a simple GET request made to the /messages/compose
endpoint and it will send a message on behalf of our first test account to our second test account ([email protected]
) with the message “test”
In a real-world attack scenario, an attacker might:
send this as a link to the victim through social engineering
include this in the vulnerable web app as an iframe, image src, link, etc as long as the SameSite cookie doesn’t interfere (everyone who visits that page with the iframe/image/… or clicks on the link, will send the message)
or send a link to an attacker-controlled page that auto-submits an HTML form (more on this later)
CSRF Exploitation bypasses:
Method-based CSRF:
We’ve seen what a basic CSRF PoC can look like however chances of finding such simple cases are very rare
And GET requests are almost never used to perform an action so developers usually fall back to using a POST/PUT/PATCH request
The thing here is, they sometimes forget to remove other accepted HTTP methods or are just unaware of the capabilities of the technology they use (CMS, framework, library to parse requests, …)
And this can open a gap for us to still exploit CSRF by changing the request method, let’s look at an example below:
In the image above, we can see a PUT request getting sent to the API
And we can’t just send a PUT request using an HTML form (not possible) or Ajax request without triggering CORS (another browser security feature that allows you to declare what resources are allowed to be shared on other origins)
But on further inspection, you can notice that you can change the request method to GET
and supply all the body parameters in the URL query
We can easily send this link and it would send the message on the victim’s behalf:
Some frameworks like Laravel and Ruby on Rails support parameters or request headers that allow you to override the HTTP method!
This means sending a request like /api/xyz?_method=POST
will send a GET request but will be parsed as a POST request on the server-side!
On Ruby on Rails you can use the X-HTTP-Method-Override: GET
request header, on Laravel: _method=GET
Content-Type-based CSRF:
Most REST APIs only make use of JSON data in requests
And thing is, if you try to replicate the request using an HTML form, you’ll notice that it is not possible to do so using application/json
content-type
Even if you try using an AJAX request, a pre-flight request will be made and will block you because of Cross-Origin Resource Sharing (CORS)
In this scenario, you’d need to try and enumerate any other content types that the endpoint may accept
For example, changing application/json
request body to application/x-www-form-urlencoded
may look like this:
This would enable us to still perform the CSRF using an HTML form as no CSRF token is used (because developers initially thought that using JSON is sufficient to mitigate CSRF):
In some cases, you may notice that only a JSON object is allowed but any content type is accepted
In this case, you’d want to make use of the text/plain
content-type inside an HTML form:
In the image above, we need to split up the JSON object to make it fit in an HTML form, typically, it’ll add a = between the name and value of the parameter
We fixed this by inserting it in the message field
Lack of CSRF token validation:
I’ve seen cases where a CSRF token is present (in the request header or as a parameter) but isn’t validated at all
This opens up a lot of possibilities for us, for example, we could:
Remove the CSRF parameter or header completely and send the request
Change it with a guessable value like “1” or “batman” or anything else
Change it with another value as long as it is the same length as the original CSRF token
Change it with another CSRF token (one of your second account or a previously generated CSRF token)
To provide some more context on the last case: this is usually because the CSRF token is not tied to the user’s session
Instead, developers decided to add all generated tokens (that are active) and save them in a database
And that means that any generated token can be used by any user (as long as the token is present in the database)
Some developers assume that CSRF tokens can’t be modified and often don’t escape it whenever they reflect it in the response (when an invalid token is sent for example)
I found an XSS some time ago, so make sure you check for it too (and don’t limit yourself, try to transform request headers into query/body parameters to get a working proof of concept 😉)!
Cookie-based CSRF:
Another approach that some developers take to mitigate CSRF attacks is generating the token and saving it in a cookie + supplying it in the request body
And on the server side, both values get compared
If both tokens match, then the action is allowed. Otherwise, your request gets rejected
Now, this approach is generally not recommended as you can create a cookie yourself using a CR/LF injection (or some other function that allows you to set cookies) and supply the same token in the body as well, like in the image below:
To further explain what’s happening here is:
First of all, we make use of a CRLF Injection to inject the
Set-Cookie: CSRF_TOKEN={CSRF_TOKEN}
in the response body (the CSRF token can be a previously generated one or any token that has the same character length). This will set the cookie for us and we use an image src for it as a simple GET request is requiredNext, it will submit the request using the same token as the one set in the cookie
Login/logout CSRF:
You’ll often notice that you can log yourself out by visiting an endpoint or a route, for example /account/logout
This is called “Logout CSRF”, the same as when logging in via OAuth for example (when you opt-in for your credentials to be saved), this CSRF issue is called “Login CSRF”
Login/Logout CSRF are generally not considered to be an issue in bug bounty as all that matters is the impact — which is none at the moment (unless otherwise mentioned by the program)
However, this bug is perfect for chaining it with a stored self-XSS!
I won’t go into depth here but I will link down an article that goes over this attack scenario
The lesson here is that “useless CSRFs” are not always “useless”, sometimes you’ll need to chain multiple bugs together to achieve the most impact
Referer header CSRF:
I personally haven’t encountered this before but apparently, there are developers that check if a request is valid if the referer header is matched
And the way to exploit this is by trying to match the loosely set regex pattern
For instance, you could try:
and make sure the target’s domain is present in the URL query parameter or path:
https://attacker.com/target.com/x?target.com
or try to use the target’s domain name as the subdomain:
https://target.com.attacker.com/x
Sometimes there’s no handling done at all when the referer header is not set
In that case, it is always a good idea to remove it from the request by setting the following tag in the head element of your proof of concept:
<meta name="referrer" content="no-referrer">
I recommend trying out all different methods and combining these (such as change content-type + request method)!
I also recommend using a CSRF exploit generator to easily create proof of concepts. Burpsuite has a built-in one but only if you have the professional version. I’m sure that ZAProxy has it as well
Resources worth reading:
CSRF guide on exploitation + various PoCs by HackTricks
Another CSRF exploitation guide (this one includes a cheatsheet to check if your target is vulnerable to CSRF) by Cobalt
CSRF lab to practice exploitation by Portswigger Academy
Some more CSRF Examples by BrightSec
6 CSRF Bypasses explained by Shahmeer Amir
A chain of Self-XSS + Login/Logout CSRF to account takeover by Renwa
A list of disclosed reports of real-world CSRF examples on Hackerone Hacktivity
Account takeover via CSRF by sharing a cat picture by Matti Bijnens
If you're interested in levelling up your bug bounty game, I can help you:
Get $200 in Digital Ocean credits to set up your Virtual Private Server for recon:
Help automate your vulnerability and recon scans with less effort using Nova Security:
Thank you for reading this far!
I hope you've enjoyed & learned something new from this post!
If you have any feedback, please do not hesitate to reach out! You can reply to this email directly or send me via Twitter DM!
Have a nice day and see you in the next post!
You can follow me on Twitter to receive upcoming updates on this newsletter: