Dock12/Why Cross-Site Request Forgery is not dead

Created by Luca Famà Mon, 29 Jul 2024 12:49:54 +0100 Modified Mon, 29 Jul 2024 12:49:54 +0100

Cross-Site Request Forgery is a common vulnerability in web applications that allows an attacker to force the victim to perform unwanted actions.

For example let’s assume that a bank web application allows users to transfer money by using the following GET request:

GET http://bank.com/transfer.do?acct=BOB-ID&amount=100 HTTP/1.1
Host: bank.com
...

where the user can specify the receiving account ID (BOB-ID) and the amount of money to transfer (100).

If the user is authenticated (i.e. he has a valid session cookie stored in the browser) an attacker could craft a malicious link that if clicked by the victim, will trigger a money transfer to the attacker account. A possible link (hosted in a malicious website) could be the following:

<a href="http://bank.com/transfer.do?acct=ATTACKER-ID&amount=100000">View my Pictures!</a>

A clever attacker could also use an <img> tag to trigger the victim to perform the action, without the need of clicking on a link (and without even noticing, because it happened automatically in the background).

<img src="http://bank.com/transfer.do?acct=ATTACKER-ID&amount=100000" width="0" height="0" border="0">

Fortunately, this attack nowadays is more difficult to exploit, thanks to several mitigations:

  • anti-CSRF tokens: this is a unique (and unpredictable) random token that is generated by the server for each user session. Each form (or action) that has some effect on the web application, must include this value, otherwise the action is rejected. This token guarantees that actions are only started by a legit user and many framework for web development implement this protection out of the box
  • SameSite cookie: cookies can include a SameSite flag. This flag can have 3 different values: strict, lax and none. Depending on which value is set, this flag controls how the browser will include cookies in cross-site requests. If properly configured, this flag can mitigate CSRF attacks
  • Using authorization headers instead of cookies: backend APIs often check authorization permissions by looking at the authorization headers. When retrieving data from a backend service, web applications often use asynchronous requests for example by using fetch or XMLHttpRequest and the authorization header is manually attached to the request. This approach can mitigate classic CSRF attacks (because they only apply when exploiting session cookies)

So it seems that CSRF attacks are now less relevant that in the past, right? Well, it depends..

Client-Side Path Traversal to exploit CSRF

Another common web application attack is Path Traversal. This vulnerability allows an attacker to access files or directory that are outside the web root folder which will lead to read arbitrary files on the affected system.

The following PHP code it’s vulnerable to this attack:

<?php>
$template = 'blue.php';
if ( is_set( $_COOKIE['TEMPLATE'] ) )
   $template = $_COOKIE['TEMPLATE'];
include ( "/home/users/phpguru/templates/" . $template );
?>

For example an attacker could set the TEMPLATE cookie to something like ../../../../../../../../../etc/passwd which will retrieve the content of the /etc/passwd file. The attacker is basically abusing the ../ characters to traverse the file system and navigate to the root directory, and then accessing the /etc/passwd file.

This attack require a vulnerability on the backend side. However, a similar technique can be used on the frontend side, to bypass the CSRF protections.

Modern web applications architecture are often build in a way that looks like this:

img1

So usually, the frontend requests data to the relevant backend APIs. Since the requested data can include user input, if the frontend doesn’t perform any validation on the user input, an attacker might be able to trick the victim to perform unwanted action, bypassing all the CSRF protections and leading to CSRF vulnerabilities.

For example, let’s take a look at this vulnerable snippet code that was found in RocketChat (version 6.5.8). This code will run (client-side) when the user navigates to the URL /marketplace/private/intall?id=1234&url=https://google.com

const appId = useSearchParameter('id');
const queryUrl = useSearchParameter('url');
const [installing, setInstalling] = useState(false);
const endpointAddress = appId ? `/apps/${appId}` : '/apps';
const downloadApp = useEndpoint('POST', endpointAddress);

We can see that the id parameter is under control of the user. A legit user request could be the following:

GET /marketplace/private/intall?id=12345&url=https://google.com HTTP/1.1
Host: rocket-chat-host
...

which will trigger the client-side code to send the following request to the backend:

POST /api/apps/1234 HTTP/1.1
Host: rocket-chat-host
...

However a malicious user could set the id parameter to something like ../../../any_endpoint and perform a CSRF attack on a chosen endpoint. For example, the attacker can trick the victim to logout, by sending the following payload:

https://rocket-chat-host/marketplace/private/install?id=../../../api/v1/users.logoutOtherClients&url=htt
ps://google.com

Instead of just logging out the victim, a real attacker could find more dangerous sinks (actions) by choosing an appropriate endpoint (for example by elevating its own account as admin, or by deleting another account, and so on..).

This technique (published by Doyensec) is really interesting because it bypasses any anti-CSRF mitigations. Infact, the the client-side code will include any anti-CSRF token and any authorization header to the request (because the code expects the request to be legit).

In order to fix this kind of vulnerability, developers should:

  • validate user input on the front-end side against path traversal attacks, especially when user input it’s used to build a path for subsequent requests
  • validate the JSON schema on the backend. This way it’s more difficult for an attacker to craft valid requests

References