$npx -y skills add elementalsouls/Claude-BugHunter --skill hunt-corsHunt CORS Misconfiguration — origin-reflection with credentials, null-origin trust, subdomain-regex bypass (unanchored vs unescaped-dot vs prefix-only), pre-flight (OPTIONS) gating bypass, postMessage origin checks. High only when an attacker-controlled origin can perform a CREDE
| 1 | # HUNT-CORS — Cross-Origin Resource Sharing Misconfiguration |
| 2 | |
| 3 | ## What actually pays (and what does not) |
| 4 | |
| 5 | CORS pays High **only** when an attacker-controlled origin can perform a |
| 6 | **credentialed** cross-origin read of sensitive authenticated data, and you |
| 7 | have a browser PoC proving the response body is readable from `evil.com`. |
| 8 | |
| 9 | Two hard browser rules that kill most "findings" — check these FIRST: |
| 10 | |
| 11 | - **`Access-Control-Allow-Origin: *` CANNOT be combined with credentials.** |
| 12 | If the server returns `ACAO: *`, the browser refuses to send/expose the |
| 13 | response for a `credentials: include` request. A wildcard-only endpoint is |
| 14 | **not** credential-exploitable. It is only interesting if the data it serves |
| 15 | is sensitive *without* a session (rare) — usually this is Informational/Low. |
| 16 | - **`Access-Control-Allow-Credentials: true` is meaningless on its own.** It |
| 17 | matters only if `ACAO` reflects/allows your specific attacker origin AND a |
| 18 | cross-origin credentialed `fetch` actually returns a readable body. ACAC on a |
| 19 | response that does not reflect your origin proves nothing. |
| 20 | |
| 21 | If you cannot demonstrate a readable cross-origin authed body in a real |
| 22 | browser, you do not have a High. Do not submit header-diffing alone. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Crown Jewel Targets |
| 27 | |
| 28 | - **Reflect-any-origin + credentials** — server echoes the `Origin` header AND |
| 29 | sets `ACAC: true` → any site reads authed API responses. The classic High. |
| 30 | - **Null-origin trust** — `ACAO: null` + `ACAC: true`. A `sandbox` iframe (or a |
| 31 | `data:`/redirect chain) emits `Origin: null`, so any page can read authed data. |
| 32 | - **Subdomain-regex bypass** — trusted-origin regex with a parsing flaw. The |
| 33 | correct payload depends on *which* flaw (see Phase 3 — this is where most |
| 34 | skills get it wrong). |
| 35 | - **Subdomain takeover → trusted origin** — a dangling subdomain that the CORS |
| 36 | policy trusts; take it over, host the PoC there (see hunt-subdomain). |
| 37 | - **postMessage missing/loose origin check** — handler that processes |
| 38 | `event.data` without strictly validating `event.origin`. |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ## Attack Surface Signals |
| 43 | |
| 44 | ``` |
| 45 | Any endpoint returning an Access-Control-Allow-Origin header |
| 46 | API endpoints: /api/*, /v1/*, /graphql |
| 47 | Profile/account: /api/me, /api/profile, /api/user, /api/session |
| 48 | Secrets/tokens: /api/tokens, /api/keys, /api/csrf, /api/account/settings |
| 49 | Financial: /api/balance, /api/transactions |
| 50 | Admin/internal: /api/admin/*, /api/internal/* |
| 51 | ``` |
| 52 | |
| 53 | Prioritize endpoints that (a) require a session cookie and (b) return PII, |
| 54 | tokens, CSRF tokens, or other secrets in the body. |
| 55 | |
| 56 | --- |
| 57 | |
| 58 | ## Step-by-Step Hunting Methodology |
| 59 | |
| 60 | ### Phase 1 — Discover CORS endpoints |
| 61 | ```bash |
| 62 | # Probe API endpoints. Use GET (not -I): some servers only emit CORS on GET, |
| 63 | # and -I sends HEAD which may be handled differently. |
| 64 | while read url; do |
| 65 | result=$(curl -s -D - -o /dev/null "$url" \ |
| 66 | -H "Origin: https://evil.com" \ |
| 67 | -H "Cookie: $SESSION_COOKIE" | grep -i "access-control") |
| 68 | [ -n "$result" ] && echo "=== $url ===" && echo "$result" |
| 69 | done < recon/$TARGET/api-endpoints.txt |
| 70 | |
| 71 | # httpx bulk check |
| 72 | cat recon/$TARGET/live-hosts.txt | awk '{print $1}' | \ |
| 73 | httpx -H "Origin: https://evil.com" -match-string "access-control-allow-origin" |
| 74 | ``` |
| 75 | |
| 76 | ### Phase 2 — Reflect-any-origin + null origin |
| 77 | ```bash |
| 78 | # Does the server reflect an arbitrary Origin back? |
| 79 | curl -s -D - -o /dev/null https://$TARGET/api/me \ |
| 80 | -H "Origin: https://evil.com" \ |
| 81 | -H "Cookie: $SESSION_COOKIE" | grep -i "access-control" |
| 82 | |
| 83 | # Vulnerable (the High case): |
| 84 | # Access-Control-Allow-Origin: https://evil.com <- reflects attacker origin |
| 85 | # Access-Control-Allow-Credentials: true <- + credentials => readable |
| 86 | # |
| 87 | # NOT exploitable for credentialed theft: |
| 88 | # Access-Control-Allow-Origin: * <- browser blocks creds read |
| 89 | # (no ACAC, or ACAC absent) <- not credentialed |
| 90 | |
| 91 | # Null-origin trust |
| 92 | curl -s -D - -o /dev/null https://$TARGET/api/me \ |
| 93 | -H "Origin: null" \ |
| 94 | -H "Cookie: $SESSION_COOKIE" | grep -i "access-control" |
| 95 | # Looking for: Access-Control-Allow-Origin: null + ACAC: true |
| 96 | ``` |
| 97 | |
| 98 | ### Phase 3 — Subdomain / trusted-origin regex bypass |
| 99 | The right payload depends on **which** regex flaw the server has. Identify the |
| 100 | class first, then send the matching payload. Getting this wrong wastes the test |
| 101 | and produces false negatives. |
| 102 | |
| 103 | | Server regex (intended: trust `*.target.com`) | Flaw | Bypass origin that matches | Why | |
| 104 | |---|---|---|---| |
| 105 | | `^https?://.*\.target\.com$` | **None** — escaped dot + end-anchor. Correct. | (no simple bypas |