Introduction

A couple of years ago, CORS misconfigurations were the hot stuff for Bug Bounty hunters. Now, the vulnerability has made its way to penetration tests reports, along with CSRF and other self-XSS poor man's security issues. The thing is, the state of the art of security for browser implementations has been evolving quite a lot in the past few years, and what was vulnerable then is probably not broken anymore (SameSite for the win).

During my previous AppSec engineer experience, I have received countless penetration tests reports notifying for CORS or CSRF vulnerabilities on different scopes, which were not exploitable, because no cookie was used (duh), or SameSite was set. Although they are widely known, and not particularly recent vulnerability categories, even high profile consulting firms would just keep providing erroneous CSRF reports.

Even if, very nice CSRF methodologies already exist, they do not take the most recent advances of browser security into account.

A recent debate with a fellow researcher made me realize that these vulnerabilities are widely misunderstood by an ever growing population of pentesters, which is not surprising as browser security is getting more an more confusing. This article, aims to clarify which factors are necessary to perform CSRF attacks in March 2023 and when recent browsers are in use. A second article will follow in a few weeks, as I feel like CORS misconfigurations deserve an article of their own.

CSRF (or XSRF) - Cross Site Request Forgery

Let's go back to the basics. According to the OWASP website:

CSRF is an attack that tricks the victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on the victim’s behalf [...]. For most sites, browser requests automatically include any credentials associated with the site, such as the user’s session cookie, IP address, Windows domain credentials, and so forth. Therefore, if the user is currently authenticated to the site, the site will have no way to distinguish between the forged request sent by the victim and a legitimate request sent by the victim.

Let us consider the following scenario: an attacker wants to gain access to the "https://victim.com" website, that runs a vulnerable version of an opensource CMS, where the attacker has spotted a CSRF vulnerability: the account creation feature is not properly protected. The steps for the attack are as follows:

  1. The attacker hosts a malicious page at "https://attacker.com", containing a form that performs a POST request "https://victim.com", in order to add a new account on the platform.
  2. The attacker sends an email containing a link to the evil webpage to the admin of the CMS.
  3. If the admin of https://victim.com is already authenticated on the website and clicks on the link, the malicious form will be submitted automatically by the browser, along with the session credentials, successfully creating a new account of the attacker on the CMS.
  4. Attacker is happy, defaces the website with cats pictures, and profits.

Well you most probably already knew all of this. Now, let us dive into the details, and talk about what could go wrong for our attacker.

Session credentials

First things first. To be successful, a CSRF attacks relies on the fact that the browser sends the session credentials automagically. If they are not sent, the action will most likely fail, as the request will not be authenticated, right? The following session credentials are sent cross-domain automatically by the browser:

Therefore, CSRF attacks cannot be performed if:

These simple check would have eliminated 80% of the false-positive CSRF reports that I received: please be a good person: do it.

SameSite

SameSite is a cookie flag which was introduced a few years ago, and allows to prevent the browser to send cookies cross-domain by default when submitting a form. It can be set to one of the following values (source):

Most of the recent browsers now enforce Lax by Default which is a tremendous improvement towards eradicating CSRF issues, as it will prevent the cookie from being sent with POST requests, although GET requests will still embed the cookies. However, the following browsers are still behind regarding this feature:

To test your browser's behavior, you can use this site. If everything is green, good news, else, investigate your choices in life (no offense to Firefox users, I am one of yours).

If all of this was not confusing enough, the "Lax By Default" features come with a small twist: it is not enforced if the cookie has been issued less that two minutes ago. Yes, you read that right. This behavior is named "Lax-Allowing-Unsafe", and "should be considered a temporary, transitional measure only".

This means that if you can force a user to authenticate again and set a new cookie in a way or another, you may manage to bypass the "Lax by Default" feature. Why was this subtle feature implemented? Most likely not to break half of the Internet while enforcing the policy by default... Somehow we will get there.

HTTP methods

CSRF attacks can be carried out using two methods:

Only the following HTTP requests will embed session credentials cross-domain:

We will omit the last three for the rest of the article as most of interesting CSRF exploits involve GET or POST methods.

HTTP forms only support GET and POST methods. Similarly, AJAX requests will embed session credentials only if these methods are used: forget about CSRF if the endpoint only allows PUT or DELETE methods, unless one of these bypasses works, but let's keep it simple. This policy can also be overriden by CORS, but we will come back to this in another article.

Content-Type

In the case of a GET requests, there is no restriction regarding the Content-Type. However, it is quite rare to stumble across features that induce state changes through GET requests nowadays, but let us keep an open mind.

For the session credential to be sent by the browser, in the case of POST requests, the Content-Type accepted by the endpoint must be one of the following:

If the endpoint you want to attack does not accept these Content-Types (for instance an application/json API only), you also can forget about CSRF, although, once again, bypasses may exist.

CSRF protections

Stating the obvious, there are many ways to implement CSRF protection, which can prevent these attacks from succeeding:

As always, bypasses may exist, and it is your job to check that these countermeasures work as intended.

CSRF - Lessons learned

Exploiting CSRF is not that easy nowadays, especially because of the implementation of the SameSite cookie parameter by browsers: many parameters have to be taken into account.

To make things easier, here is a flow-chart attempting to summarize all of the previous considerations that were enumerated in this article:

CSRF

Closing words (PoC, or it didn't happen)

Everything is in the title. If you are not sure if the feature is vulnerable to CSRF, produce a PoC. There is no shame in that. Your customers will be happy as it will be easier for them to understand what is going on. This can be done in two clicks using BurpSuite Pro. As a security professional, you just have no excuse!

Stay tuned for the second article of this series, which will focus on CORS misconfigurations!

Need offensive security services, or application security support? CryptID has got you covered