Physical attacks
Mobile Apps Pentesting
Pentesting

CORS - Misconfigurations & Bypass

What is CORS

The CORS standard is needed because it allows servers to specify who can access its assets and which HTTP request methods are allowed from external resources.

In a same-origin policy, is needed that both the server requesting a resource and the server where the resource is located uses the same protocol (http://),domain name (internal-web.com) and the same port (80). Then, if the server forces the same-origin policy, only web pages from the same domain and port will be able to access the resources.

Headers

The following are the new HTTP headers added by the CORS standard:

  • Access-Control-Allow-Origin

When the value is * it means that any origin can access the resources.

  • Access-Control-Allow-Credentials

If the value is set to true then the browser will send credentials (cookies, authorization headers or TLS client certificates).

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
fetch(url, {
credentials: 'include'
})
  • Access-Control-Allow-Methods

Specifies which HTTP request methods (GET, PUT, DELETE, etc.) can be used to access resources.

  • Access-Control-Allow-Headers

  • Access-Control-Expose-Headers

  • Access-Control-Max-Age

  • Access-Control-Request-Headers

  • Access-Control-Request-Method

  • Origin

PRE-FLIGHT REQUESTS

Note that in a GET request no pre-flight request is sent (the get request is sent directly), but if you want to access the headers/body of the response, it must contains an Access-Control-Allow-Origin header.

As mentioned before, most servers will allow GET requests but may block requests to modify resources on the server. Servers don’t just blindly block such requests though; they have a process in place that first checks and then communicates to the client (your web browser) which requests are allowed.

When a request is made using any of the following HTTP request methods, a standard preflight request will be made before the original request.

  • PUT

  • DELETE

  • CONNECT

  • OPTIONS

  • TRACE

  • PATCH

Preflight requests use the OPTIONS header. The preflight request is sent before the original request, hence the term “preflight.” The purpose of the preflight request is to determine whether or not the original request is safe (for example, a DELETE request). The server will respond to the preflight request and indicate whether or not the original request is safe. If the server specifies that the original request is safe, it will allow the original request. Otherwise, it will block the original request.

The request methods above aren’t the only thing that will trigger a preflight request. If any of the headers that are automatically set by your browser (i.e., user agent) are modified, that will also trigger a preflight request.

Exploitable misconfigurations

Notice that most of the real attacks require Access-Control-Allow-Credentials to be set to true because this will allow the browser to send the credentials. Without credentials, many attacks become irrelevant; it means you can't ride on a user's cookies, so there is often nothing to be gained by making their browser issue the request rather than issuing it yourself.

One notable exception is when the victim's network location functions as a kind of authentication. You can use a victim’s browser as a proxy to bypass IP-based authentication and access intranet applications. In terms of impact this is similar to DNS rebinding, but much less fiddly to exploit.

From server controlled by the attacker

Imagine a web application where a path (/User_details) returns all the confidential information about yourself. IfAccess-Control-Allow-Originis set to * and Access-Control-Allow-Credentialsis set to true, then a user could create a malicious JS in his website and make everybody access his details and steal them.

Basic bypasses

In the real world this cannot happen as this 2 values of the headers are forbidden together. It is also true that a lot of developers want to allow several URLs in the CORS, but subdomain wildcards or lists of URLs aren't allowed. Then, several developers generates the Access-Control-Allow-Originheader dynamically, and in more than one occasion they just copy the value of the Origin header.

In that case, the same vulnerability might be exploited.

In oder cases, the developer could check that the domain (victimdomain.com) appears in the Origin header, then, an attacker can use a domain called attackervictimdomain.com to steal the confidential information.

The null Origin

"null" is a special value for the Origin header. The specification mentions it being triggered by redirects, and a few stackoverflow posts show that local HTML files also get it. This is nice because several application will allow this value inside the CORS and any website can easily obtain the null origin using a sandboxed iframe:

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data:text/html,<script>*cors stuff here*</script>’></iframe>

Advanced bypasses

Most of the regex used to identify the domain inside the string will focus on alphanumeric ASCII characters and .- . Then, something like victimdomain.com{.attacker.com inside the Origin header will be interpreted by the regexp as if the domain was victimdomain.com but the browser (in this case Safari supports this character in the domain) will access the domain attacker.com .

The _ character (in subdomains) is not only supported in Safari, but also in Chrome and Firefox!

Then, using one of those subdomains you could bypass some "common" regexps to find the main domain of a URL.

For more information and settings of this bypass check: https://www.corben.io/advanced-cors-techniques/ and https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

From XSS inside a subdomain

One defensive mechanism developers use against CORS exploitation is to white-list domains that frequently requests access for information. However, this isn’t entirely secure, because if even one of the subdomains of the whitelisted domain is vulnerable to other exploits such as XSS, it can enable CORS exploitation.

Let us consider an example, the following code shows the configuration that allows subdomains of requester.com to access resources of provider.com.

if ($_SERVER['HTTP_HOST'] == '*.requester.com')
{
//Access data
else{ // unauthorized access}
}

Assuming that a user has access to sub.requester.com but not requester.com, and assuming that sub.requester.com is vulnerable to XSS. The user can exploit provider.com by using cross-site scripting attack method.

Server-side cache poisoning

If the stars are aligned we may be able to use server-side cache poisoning via HTTP header injection to create a stored XSS vulnerability.

If an application reflects the Origin header without even checking it for illegal characters like \r, we effectively have a HTTP header injection vulnerability against IE/Edge users as Internet Explorer and Edge view \r (0x0d) as a valid HTTP header terminator:GET / HTTP/1.1 Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

Internet Explorer sees the response as:

HTTP/1.1 200 OK Access-Control-Allow-Origin: z Content-Type: text/html; charset=UTF-7

This isn't directly exploitable because there's no way for an attacker to make someone's web browser send such a malformed header, but I can manually craft this request in Burp Suite and a server-side cache may save the response and serve it to other people. The payload I've used will change the page's character set to UTF-7, which is notoriously useful for creating XSS vulnerabilities.

Client-Side cache poisoning

You may have occasionally encountered a page with reflected XSS in a custom HTTP header. Say a web page reflects the contents of a custom header without encoding:GET / HTTP/1.1 Host: example.com X-User-id: <svg/onload=alert(1)> HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Headers: X-User-id Content-Type: text/html ... Invalid user: <svg/onload=alert(1)>

With CORS, we can send any value in the Header. By itself, that's useless since the response containing our injected JavaScript won't be rendered. However, if Vary: Origin hasn't been specified the response may be stored in the browser's cache and displayed directly when the browser navigates to the associated URL. I've made a fiddle to attempt this attack on a URL of your choice. Since this attack uses client-side caching, it's actually quite reliable.

<script>
function gotcha() { location=url }
var req = new XMLHttpRequest();
url = 'https://example.com/'; // beware of mixed content blocking when targeting HTTP sites
req.onload = gotcha;
req.open('get', url, true);
req.setRequestHeader("X-Custom-Header", "<svg/onload=alert(1)>")
req.send();
</script>

Bypass

XSSI (Cross-Site Script Inclusion)

XSSI designates a kind of vulnerability which exploits the fact that, when a resource is included using the script tag, the SOP doesn’t apply, because scripts have to be able to be included cross-domain. An attacker can thus read everything that was included using the script tag.

This is especially interesting when it comes to dynamic JavaScript or JSONP when so-called ambient-authority information like cookies are used for authentication. The cookies are included when requesting a resource from a different host. BurpSuite plugin: https://github.com/kapytein/jsonp

Read more about the difefrent types of XSSI and how to exploit them here.

Easy (useless?) bypass

You can ask a web-application to make a request for you and send back the response. This will bypass the the Access-Control-Allow-Origin but notice that the credentials to the final victim won't be sent as you will be contacting a different domain (the one that will make the request for you).

CORS-escape provides a proxy that passes on our request along with its headers, and it also spoof the Origin header (Origin = requested domain). So the CORS policy is bypassed. The source code is on Github, so you can host your own.

xhr.open("GET", "https://cors-escape.herokuapp.com/https://maximum.blog/@shalvah/posts");

Proxying is kinda like “passing on" your request, exactly as you sent it. We could solve this in an alternative way that still involves someone else making the request for you, but this time, instead of using passing on your request, the server makes its own request, but with whatever parameters you specified.

DNS Rebinding

Basically you make the victim access your page, then you change the DNS of your domain (the IP) and make it points to your victims web page. You make your victim execute (JS) something when the TLS is over so a new DNS request will be made and then you will be able to gather the information (as you will always mantains the user in your domain, he won't send any cookie to the victim server, so this options abuses the speciall privileges of the IP of the victim).

Also, I don't know why this attack plays with the TLS of the DNS instead of just having a subdomain always pointing to the victims IP.

Tools

Fuzz possible misconfigurations in CORS policies

References

CORS haders: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS