Table of Contents
This page was originally written for my blog and has been pasted here for completeness.
What is CSRF Protection?
CSRF (Cross Site Request Forgery) protection is an important feature which should be added to all websites which allow users to sign up or send data. Without CSRF protection, attackers can trick a web browser into to sending invalid data to servers on behalf of a victim. A very basic example of a CSRF attack is redirecting a victim to example.com/delete-account and then the victim’s account being deleted because the server trusted the request from the victim. Real attacks are slightly more complicated because data is usually sent via HTTP(S) POST requests so a simple redirect isn’t enough. The basic principal is the same though.
These attacks work because the server ‘trusts’ the web browser because it has all the correct authentication cookies. Servers usually assume that when a web browser has sent a request, the request was authorised by the user.
A common method to protect web forms from CSRF vulnerabilities is by using a CSRF Token which is a randomly generated code which the server sends to the browser along with a web form. When the user submits the form, the code is sent back to the server. If the code isn’t submitted with the form data (or is changed in some way), the request gets completely blocked and the user needs to complete the form again.
This protection works because attackers are unable to see the random CSRF tokens (assuming HTTPS is in use) and therefore cannot spoof a form submission. To expand on the previous example: example.com would expect the user to visit example.com/delete-account?csrf=62738923 (where 62738923 is a randomly generated token) in order to delete their account. The random token stays secret between the website and the web browser so an attacker cannot simply redirect a victim and have their account deleted.
Correctly implemented CSRF tokens are usually enough to fully protect a website from CSRF attacks. Unfortunately, many websites do not correctly implement CSRF tokens and therefore the protection is not enough to prevent attacks. I recently saw a website where the CSRF protection was badly implemented and due to some other security weaknesses on the website, CSRF attacks were possible.
Flaw One: A single token in a cookie
When testing for vulnerabilities, I noticed the CSRF token sent to the server was never changed. Upon further inspection, I realised that a single token was generated upon sign in and stored in a cookie. This cookie (and the token) would only expire when I logged out so the same token could be used for a long period of time.
Flaw Two: Bad flags and headers
The website I was testing uses the “Strict Transport Security” header which tells the browser to always connect via HTTPS on future visits. This is important because if the user does not type https at the start of the domain, they will initially load part of the site over insecure HTTP; this header prevents that behaviour.
I noticed that the CSRF token cookie did not have the “secure” flag set. This means that if an attacker is able to monitor traffic between the victim and the internet, they can steal the CSRF token if the victim accesses the site without using HTTPS.
After more research, I noticed that this particular website had a catchall subdomain setup which meant all subdomains would connect to their servers (eg, 123456abcdefg.example.com works the same as example.com). I also noticed their “Strict Transport Security” header did not include subdomains. In addition, their CSRF token cookie was valid on all subdomains.
All of these flaws combined make it possible to steal the cookie which contains the CSRF token.
Stealing the token
If a victim connects to a WiFi access point controlled by an attacker, the attacker can force the victim to visit a random subdomain either by using a captive portal, or by hijacking a different HTTP conversation. If the attacker has full control over the network, this should be quite easy.
Once the victim has been tricked into visiting the random subdomain, their CSRF token cookie will also be sent in plain text which allows the attacker to capture it. The attacker can then redirect the victim to another domain so they will be less suspicious about the initial redirect.
Due to the lack of token regeneration, the attacker can then trick the victim into submitting forms using the stolen token as many times as they like. In this specific situation, the CSRF protection does protect against many CSRF attacks but is completely broken if an attacker can monitor/tamper with traffic from the victim.
After performing the CSRF attack, personal account information could be changed (but not viewed) by the attacker. In this specific situation, accounts could not directly be taken over. However, by changing some of the account personal information, it may be easier to trick customer service into performing a password reset.
CSRF tokens should be kept secret between the user and the website. As soon as this secrecy is lost, the CSRF protection becomes useless and CSRF attacks can be successfully made. Browsers are constantly adding new security features which make CSRF attacks harder but websites should still have a good working CSRF protection system anyway.
I cannot include any specific details about the website I was testing or any proof of concept code because the flaw still exists on the website. I did these tests as part of an official bug bounty system, but unfortunately the website does not want to fix the flaws due to them being hard to exploit (‘hard’ = an attacker needs to own a WiFi access point and get a victim to connect to it).