James Kettle’s 2016 research was instrumental in raising awareness of the deleterious effects of CORS (Cross-Origin Resource Sharing) misconfiguration on Web security. Does the story end there, though? Is writing about CORS-related security issues in 2022 futile? I don’t think so.
This post is the first in a series in which I will discuss more minor CORS-related issues and present lesser-known detection techniques. My primary audience is people on the offensive side, but folks on the defensive side may also find this series interesting.
I do not present the fundamentals of the CORS protocol here. If you need to familiarise yourself with the protocol, MDN Web Docs’ introduction is a good starting point.
Test resources, not just domains ¶
Not only can CORS be configured at different layers of the tech stack (reverse proxy, origin server, etc.), it can also be configured at different levels of granularity. For example, Express’s CORS middleware allows developers to configure CORS not just for the whole server, but also for individual routes. When hunting for CORS misconfigurations on a given domain, bear in mind that some resources may be CORS-aware and others not, and that different CORS-aware resources may have different CORS configurations. Testing for CORS awareness and misconfiguration of only a single path on your target domain would be a mistake, because you may fail to detect misconfigured CORS-aware resources on that domain.
Broaden your search for allowed origins ¶
Because the CORS protocol only tolerates a single serialized-origin value in the
Access-Control-Allow-Origin response header,
the set of allowed origins (if any) doesn’t readily reveal itself to attackers.
In contrast, the
of a page’s Content Security Policy
Historical note ¶
The W3C’s 2009 Working Draft about CORS did make provision
for a multivalued
as did RFC 6454 (entitled “The Web Origin Concept”).
However, no major browser has ever supported this feature, which led the W3C
to revise its stance in its 2013 Candidate Recommendation.
The Fetch Standard, which has since supplanted the latter
as the official specification of the CORS protocol,
does not support a multi-valued
To even detect CORS awareness, let alone infer as much of the set of trusted origins as possible, you often have no other choice but dynamic analysis, treating the server as an oracle and repeatedly asking it yes-or-no questions, one candidate origin at a time:
Client: Do you allow this origin?
Client: Ok… What about that one?
Server: Nope. Keep trying.
Client: How about this other one?
Server: Yes, I do.
A good first step, according to accepted wisdom,
consists in supplying the origin of the resource itself:
for instance, if resource
https://example.com/whatever is configured for CORS,
it likely allows its own origin,
However, I’ve run into a few cases where resource
is configured for CORS and allows origin
but does not allow origin
or any subdomain of
example.com other than
Had I not enumerated the subdomains of
and fed them to my dynamic analysis of the target,
I would likely have failed to detect that it was even configured for CORS,
because the responses to most of my probing requests didn’t contain
any CORS response headers.
Beyond subdomains, which other origins is the resource of interest likely to allow in its CORS configuration (if any)? A promising source of candidate origins is other domains owned by your target, as well as their subdomains. A couple of reverse-WHOIS searches can reveal many of those domain names.
Don’t stop there.
Meiser et al., in a fascinating 2021 paper entitled Careful Who You Trust:
Studying the Pitfalls of Cross-Origin Communication,
suggest that useful clues can be gleaned from your target’s
/crossdomain.xml (Flash) and
/clientaccesspolicy.xml (Silverlight) resources,
if those resources exist on the server.
In my experience, theirs is a good tip.
Looking those pages up on the Wayback Machine
may also yield a few additional candidate origins.
Furthermore, as Meiser et al. rightly point out, the listing of
connect-src CSP directive of
is a good indication that
https://a.com allows origin
in its CORS configuration.
Unescaped periods in the eTLD+1 part of a regexp ¶
Many CORS configurations that intend to allow multiple origins rely on some regular expression for origin validation. In some cases, the regexp is flawed, insofar as it matches more origins than intended by the developers. A common blunder consists in using unescaped periods to represent DNS label separators in the regexp.
Through dynamic analysis of your target, you may be able to infer that the developers,
in order to allow
https://example.com and arbitrary subdomains thereof,
are using something like the following regexp for origin validation:
Note that the period separating the subdomain DNS label from the eTLD+1 (
is escaped as it should, which precludes attacks from origins like
If you cannot find some XSS or subdomain takeover on a subdomain of
you should give up and focus your efforts elsewhere.
Attentive readers may have noticed the presence of an unescaped period
between the TLD (
com) and second-level domain (
Is this tantalising omission of an escape exploitable in some way?
Unfortunately, the public-suffix list contains no entry
that starts with
example followed by a single character followed by
(at least, at the time of writing this post).
Therefore, acquiring a domain name like
is impossible, unless you’re willing to go through the arduous and
expensive process of registering a brand-new
examplezcom eTLD for yourself.
However, in cases where the eTLD of the allowed origin is composed of, not just one, but multiple DNS labels, everything’s not lost. For instance, assume now that the following regexp is used for origin validation:
Note that the part of the regexp corresponding to the eTLD+1 contains two unescaped periods.
The second unescaped period (the one between
uk) is unexploitable,
for the same reason as explained above.
The first unescaped period (the one between
co) is much more promising,
uk is itself a public suffix, and a domain like
examplezco.uk, whose secure origin matches the regexp,
is probably available for purchase.
If the price for that domain isn’t prohibitive,
you can buy it and mount your attack against your target from there.
Admittedly, because public suffixes that are composed of multiple DNS labels
co.uk, in my example) and for which a less specific public suffix exists
uk) are relatively rare, exploitable cases of unescaped periods within the
eTLD+1 part of a regexp are even rarer.
To this day, neither I nor infosec superstar James Kettle
have ever come across one.
But you should leave no stone unturned.
In fact, the possibility of such a vulnerability should further spur you to broaden
your search for domains allowed by your target’s CORS configuration.
CORS vs. SameSite ¶
CORS-aware resources that are meant to allow reliable cross-site access now need
session-identifying cookies to be explicitly (i.e. rather than relying on browsers’ defaults)
However, contrary to what you may have read elsewhere,
legitimate use cases for the stricter
Strict values in conjunction with CORS
Developers who wish to protect their CORS-aware resources against cross-site attacks
but nonetheless allow (all or some) same-site origins
may indeed set their cookie with either of those two
SameSite attribute only affects cross-site requests,
same-site requests do unconditionally carry such a cookie.
If you find yourself in a situation where the cookie is marked
don’t dismiss the possibility of abuse too quickly.
Seek instances of cross-site scripting or
on those same-site origins that the CORS-aware resource trusts,
as Sam Curry once did to great effect:
This was the saving grace which let me exploit a CORS misconfig which leaked the session token. Very sad to see SameSite cookies but it’s definitely a fun new challenge :)— Sam Curry (@samwcyo) February 5, 2021
More about this subtlety in one of my previous posts.
I’d like to thank Alesandro Ortiz, who was kind enough to review a draft of this post before publication.
2022-08-04 20:05 +0000