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.
What is CORS?
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.
With that said, here are a couple of common mistakes that we have detected on other websites before.
Origin is reflected
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
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.
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.
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.