Normally I would only post this under my Cybersecurity blog, but as this can be checked during the testing cycle I thought it might work out well in the QA blog. CSRF is a vulnerability in web applications, where an attacker can send a POST to the web app from a different origin (domain). For example, if I had a CSRF vulnerable website: example1.com and it had a feature to add an administrator of the account, that feature might be exploited by CSRF. A threat actor could create the same POST (as a form action) and host it from a HTML page on their own domain (attacker1.com). The form would POST back to example1.com with all the relevant fields, adding themselves (the threat) as the administrator. All that is required is to trick a user from example1.com to open the attacker’s HTML page. Once opened, it will execute the POST and the victim will see the page redirect to examle1.com. If they have an active session the POST will occur immediately. If they they are logged out, they will get a login page (most likely), and if they login the POST will occur. This could easily lead to an account compromise.
“Why would a victim click on a page, how could they be tricked?” There are hundreds of creative ways a person could get tricked into this situation. One way could be to setup a waterhole, like a site that promoted, “are you a customer of example1.com? If so, you may be entitled to financial compensation for their mishandling of subscriptions…” That would easily attract traffic from example1.com users.
CSRF and CORS
To setup a CSRF attack, you first need to test if your site is vulnerable. There are numerous scanners that will tell you if you are lacking CSRF tokens, or CORS. CSRF tokens and CORS are both ways to require same origin on POSTS (meaning that all POSTS the web app received must be from the same domain).
Scanners like OWASP’s ZAP will quickly scan your test site/domain and return a list of alerts. If there is a missing CORS or CSRF token, it will be identified. I should note that the absence of CORS or CSRF token doesn’t necessarily mean there’s a vulnerability. Some teams use their own mitigation methods as well. But if CORS and CSRF Tokens are absent then it’s a good candidate for testing.
SETUP
This part of the article is going to go over the tools and methodology to test for CSRF.
Pre-Conditions
The first and most important part of testing is to make sure you have permission to test. This will assume you already have permission and you are testing a site for your employer or client.
Download Burp Suite from Portswigger, if you don’t have it already. Burp Suite (community edition is free) is a tool that acts as a proxy between your web traffic and your client. It can do a lot of web application tests (intercepting and changing http headers, fuzzing, etc.) but this article will only deal with monitoring the header traffic.
Scan the Application for CSRF Tokens or CORS
Open Burp Suite and click on the Proxy tab. From the proxy tab, click “open browser.” This will spawn the Burp Browser.
Once the browser opens, we can go to our test domain, and then click “intercept” to toggle it on.
When intercept is turned on, every web request captured will pause the flow. To move through each part of the handshake, we have to click “forward”.
When the site loads, we look for any indication that CORS or a CSRF Token are being used.
(optional) A bit faster to check for CORS or CSRF is to run a scan. Run ZAP against the web app to do a default attack, make sure that you have set the test domain as the only “in scope” item, so the tool doesn’t go off and attack 3rd party sites linked.
Investigate Application
Create an account and go through the web application. Look for areas of the application that have great impact without extra validation (like email verification links, etc.). Some social media sites, for example, allow users to add co-creators or even administrators to their accounts. If this can happen in a single web POST, without email verification or validation, then that would be a candidate.
The reason why we want an action that doesn’t expect a secondary verification, is that we’re firing a single POST. Think of it like a single missile. We only have one so it has to hit a target in a powerful place, and the attack needs to happen in this one action. If the action triggers an email to verify the action, we obviously don’t have access to the account email yet, so it would be pending a user to verify the action in the email (which likely will never happen).
Capture the Data
Once you find an action that takes one POST/DELETE to do something powerful, then we move on to making the test for the vulnerability (the exploit.)
Use Burp Suite and open the Burp Browser (under proxy). Navigate to the location in the app where the POST occurs. Now turn on “Intercept” in Burp Suite. Fill out the form in the app and submit the action.
Burp will capture all the fields and values. This is important because we’re going to build our own form with our own values.
When you capture a POST or form submit, Burp will have the data of the fields listed in the capture. The capture will look something like this:
POST [the url or page used to make the post]
[a bunch of HTTP headers]
firstname=Test&lastname=Tester®_email__=6234510021®_email_confirmation__=®_passwd__=MakeMyDay&birthday_month=5&birthday_day=21&birthday_year=1915
The important parts for our test is the first line, which shows what endpoint is being used in the POST and the last line, showing the fields and values being passed through.
Create the Exploit
Once we know the fields and values passed, we can create our fake form. The form itself will be hidden, so all we have to do is have someone load the page and the form will automatically submit.
<form action="https://[domain found in burp suite]/myaccount/useradmin/adduser" method="POST">
<input type="hidden" name="firstname" value="Naughty" />
<input type="hidden" name="lastname" value="User" />
<input type="hidden" name="pass" value="MakeMyDay" />
<input type="hidden" name="birthday_month" value="5" />
<input type="hidden" name="birthday_month" value="21" />
<input type="hidden" name="birthday_month" value="1915" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
In the above example, we have a form with all hidden fields. These fields match the POST that was captured in Burp.
The script below the form will auto submit the form when the page loads.
Hosting the Exploit
The HTML above will not work from your own local device. It needs to be hosted.
It will require too much effort to tell you how to create a web server. Basically you need a web server that can reach the target. If your target is in a test environment behind a firewall, you need your attack host to have a web server behind the same firewall so it can talk to the test target.
Once the page is hosted on a web server (Apache, etc.) we can try the attack.
Testing the Attack
With a valid user, logged in to the web app, go to your hosted exploit in the same browser.
Result: going to the hosted exploit should redirect to the application, and the POST should execute. Check the victim’s account and verify the action was successful (adding a user, changing owners, etc.)
With a valid user, not logged in to the web application, go to your hosted exploit in the same browser.
Result: going to the hosted exploit should redirect to the application, and since the user isn’t logged in, they will likely get a login page. As the victim, login. After login the POST should have executed. Check the victim’s account to see if the action was successful.