How the attack works

Browsers send an Origin header along with cross-origin requests. The server uses the response headers Access-Control-Allow-Origin (ACAO) and Access-Control-Allow-Credentials (ACAC) to decide which foreign origin may read the response and whether cookies/credentials may be sent along with it.

By default, browsers do not send credentials with cross-origin requests (MDN). Misconfigurations arise when the server has one of the following weaknesses:

  • Unchecked reflection: the server reflects the origin supplied by the client back into ACAO without checking it.
  • Flawed whitelist checks: whitelist checks are implemented incorrectly via prefix/suffix/regex matching (e.g. "ends with normal-website.com" also allows the attacker-registered domain hackersnormal-website.com, "starts with normal-website.com" also allows normal-website.com.evil-user.net).
  • Whitelisting null: the server whitelists the special value null, which an attacker can generate via a sandboxed iframe.

If the server additionally sets ACAC: true, the attacker's page can make authenticated requests via fetch/XMLHttpRequest with withCredentials=true and read the victim's response.

Important according to MDN: for requests with credentials, the server must specify a concrete origin instead of the wildcard *. In addition, the Origin header can be freely forged by non-browser clients such as curl, which is why, according to OWASP, it must not be used for authentication.

1. Starting pointAPI reflects the origin or allows credentials too broadly
→
2. AttackA foreign website sends a request with cookies
→
3. ResultAuthenticated response gets stolen

Overly permissive CORS lets foreign sites read authenticated data.

Example

Request
GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...
Response
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...

Impact

A foreign, attacker-controlled website can read authenticated responses from the API on behalf of a logged-in victim, and thereby steal sensitive data such as API keys, CSRF tokens, or personal data.

According to PortSwigger, this can be escalated further:

  • Trust relationships: via trust relationships with an XSS-vulnerable subdomain.
  • Breaking TLS: by breaking TLS, if an HTTP subdomain is considered trusted.
  • Access to the intranet: with ACAO: * without credentials, as access to internal intranet resources by abusing the victim's browser as a proxy.

How to protect against it

  • Never reflect origins from the Origin header without checking them; only enter genuinely trustworthy origins in Access-Control-Allow-Origin (PortSwigger).
  • Perform whitelist checks against the exact, complete origin string, not via prefix/suffix matching or error-prone regex, to rule out bypass domains such as hackersnormal-website.com or normal-website.com.evil-user.net (PortSwigger).
  • Don't whitelist the value null; avoid Access-Control-Allow-Origin: null, since sandboxed/serialized requests can generate it (PortSwigger).
  • Don't use the wildcard * together with credentials - according to MDN, the server must specify a concrete origin for requests with credentials; don't use wildcards on the internet for sensitive/internal resources (MDN, PortSwigger).
  • Don't use the Origin header for authentication, since it can be freely forged by non-browser clients (curl/Wget/Burp) (OWASP).
  • CORS is not a substitute for server-side security: enforce authentication on the accessed resources and enforce session management and protection of sensitive data server-side (PortSwigger, OWASP).

Sources

Keep reading