Search What is Detectify?
×

CORS Misconfigurations Explained

Linus Särud / April 26, 2018

Is your CORS configuration making your web application vulnerable? When misconfigured, CORS can be bypassed in many different ways. In this article, we take a closer look at CORS misconfigurations and explain the most common mistakes that can lead to a bypass.

For security reasons, it is very important that JavaScript that is running on one domain can only read data from that very domain. If such limitations were not in place, a small snippet of JavaScript on this blog could easily open a new tab to Facebook and steal all your private messages or any other content.

This is why JavaScript (and other scripts) are only allowed to send requests and read data from the domain the request originates from. As such, it is possible to write a function in JavaScript that makes a request to the domain it is hosted on and reads the data. This is, for example, how most API calls work. The data is processed in JavaScript instead of being directly displayed on the web page.

What is CORS?

However, there are legitimate use cases of sending requests to other domains. For example, there are public APIs that allow anyone to query them and should therefore also allow JavaScript on any domain to send requests to them. In cases such as these, you need to be able to send requests to other domains. The solution is CORS, Cross-Origin Resource Sharing.

In short, CORS is a header set by the web server. It regulates exactly which domains that are allowed to send requests to it. There are a few additional tweaks, but that is the foundation of CORS. More details can be found in Mozilla’s MDN web docs.

What makes this a problem

It is possible to misconfigure CORS and thereby allow a domain controlled by a malicious party to send requests to the domain, which takes us back to square one again.

How Detectify can help

Such misconfigurations can happen in a lot of different ways, and the easiest way to check for yourself is to run a security scan with Detectify.

Detectify CORS findings

CORS findings in a Detectify report (All Tests view)

With that said, here are a couple of common mistakes that we have detected on other websites before.

Origin is reflected

Each time the browser makes a cross-domain request (a request to another domain) it adds an origin header. That header has the value of the domain the request originates from, almost like the referer header. If JavaScript on https://attacker.com/test.html makes the request, the value would be https://attacker.com.

Some websites directly reflect this value as a domain that is allowed to make requests. Doing so completely overrides the purpose of CORS, as any domain is now able to send requests to the domain.

This can happen as a result of an attempt to automate a CORS policy for several domains and mistaking origin for the domain itself.  Sometimes, the vulnerability originates from a neat bypass used during development that has been left behind.

Insufficient regular expression

Similar to the example above, some websites check the origin according to a regular expressions before using it in the response. If the website wants to allow any subdomain to make requests to the website, it performs a check that looks like this:

origin = request.getHeader("Origin")

regex = "https:\/\/[a-z]+.example.com"

if re.test(origin, regex):

response.addHeader(origin)

If the origin is https://asdf.example.com, it would be echoed as allowed origin and the request would go through.

However, in regular expressions, the dot (.) means anything. To refer to the actual dot character, it has to be escaped (often as \.). As such, https://asdfxexample.com would succeed as well. All the attacker has to do to exploit this is to buy asdfxexample.com and host the malicious code there.

Allow requests from localhost or 127.0.0.1

During development, it is not unusual for developers to allow requests from localhost to interact with the website. However, this can leak into production systems.

At first, the problem might not be obvious as it is the user that controls localhost. However, one example where this is not the case is if the user is running software vulnerable to XSS on localhost as this also exposes the website that has misconfigured CORS.

Third party hosts

There are a lot of third-party domains that anyone can upload JavaScript to. JSBin.com and CodePen.io are only two of the many examples.

Sometimes those domains are configured so that they are allowed to make requests. Although there are many reasons this is the case, the full impact is most likely forgotten.

An attacker could upload malicious JavaScript to those websites and that JavaScript can then send and read data from the misconfigured domain. Once again, back to square one as CORS has been bypassed.

Amazon S3 bypass

Similar to the example above, Amazon S3 buckets are sometimes allowed. The site owner has some application hosted at Amazon that needs to interact with the website. To do this, the owner decides to allow any S3 bucket make requests.

In this case, all the attacker needs to do is sign up for their own S3 bucket and host the malicious page.

Prefix check

Slightly related to the faulty regular expression described above, there are instances where only the beginning of the origin is checked.

For example, if the website intends to allow https://example.com, it also accepts https://example.com.attacker.com. This seems obvious once you are aware of it, but it  can easily be overlooked when writing or looking at the code.

More creative ways

The issues mentioned in this blog post are the most common ones and hopefully explain the problem in a clear and easy-to-understand way.

Of course, there are many more similar bypasses out there. Some of them are related to the things touched upon in this article, but with slightly different angles. One good write-up on something that we look for would be this one about a CORS bypass in Yahoo!View.