Skip to content

Add --open-redirect mutator (CWE-601 unvalidated redirect)#32

Merged
bugsyhewitt merged 1 commit into
mainfrom
worker-r35-possession
May 30, 2026
Merged

Add --open-redirect mutator (CWE-601 unvalidated redirect)#32
bugsyhewitt merged 1 commit into
mainfrom
worker-r35-possession

Conversation

@bugsyhewitt

Copy link
Copy Markdown
Owner

Summary

Adds the --open-redirect mutator targeting CWE-601 / ASVS V5.1.5 unvalidated redirects at the request-parameter layer.

Where --ssrf-probe (shipped R34) attacks what server-side network resource the application fetches (URL value consumed by outbound HTTP client reaching internal IPs / cloud metadata), --open-redirect attacks what destination the application bounces the caller's browser to (URL value reflected into a Location: header reaching an attacker-controlled external site). The two are disjoint by design — different vuln classes, different fixes, different finding classes.

The bug being tested: a next/redirect_uri/returnTo/callback value is echoed into a Location: header (or meta-refresh / window.location.assign) without validating that the destination is in-scope. An attacker who substitutes that value with an attacker-controlled URL turns the trusted host into a phishing-redirect surface or leaks OAuth authorization codes to attacker.example via the URL fragment.

Surfaces & techniques

Four surfaces (query, urlencoded body, top-level JSON string, Referer header when present) cross-producted with seven disjoint payload techniques (sorted, deterministic):

Technique Wire-form payload Defeats
backslash-host https://attacker.example\@target.example/ RFC-vs-browser authority-parsing disagreement
cross-origin https://attacker.example/ No validation / presence-only check
data-uri data:text/html,<script>alert(1)</script> XSS via redirect (legacy / WebViews)
javascript-uri javascript:alert(1) XSS via redirect (legacy / WebViews)
protocol-relative //attacker.example/ Same-origin-by-leading-slash defenses
userinfo-confusion https://target.example@attacker.example/ Naive substring/prefix host check
whitespace-prefix https://attacker.example/ Trim-then-pass validators

The Referer surface emits only the header-safe subset (excludes backslash-host and whitespace-prefix, which net/http would reject or silently trim from a header value).

Eligibility & invariants

  • Name match (substring, case-insensitive): back, callback, continue, dest, destination, goto, next, redir, redirect, return, returnto, success, target, url. Covers redirect_uri, redirect_url, returnTo, next_page, callback_url, etc.
  • Value shape: any absolute http(s) URL value, regardless of parameter name.
  • Referer surface fires whenever a Referer header is present.
  • Requests with no redirect-destination parameter and no Referer emit zero variants.
  • Every variant keeps the caller's own credentials (Identity == nil) — same-caller destination-rewrite probe, not an identity swap.
  • Off by default — payloads point browsers at attacker URLs and embed XSS-via-redirect shapes.

Findings are class open-redirect, severity MEDIUM (impact is phishing / OAuth-token leakage, not direct privilege bypass).

Disjoint from

  • --ssrf-probe — server-side fetch (internal targets) vs. client-side redirect (external attacker site)
  • --origin-spoof — spoofs Origin/Referer to bypass origin-validation CSRF, not to coerce a redirect destination
  • --csrf-header — forges anti-CSRF tokens, not redirect destinations

Test plan

  • go build ./... clean
  • go vet ./... clean
  • 26 new unit tests in internal/mutate/open_redirect_test.go all pass
    • off-by-default gate, nil-base safety, NOT-in-DefaultRegistry
    • eligibility (name match, value-shape match, no-eligible → zero variants)
    • per-surface coverage (query / body / json / Referer)
    • Referer surface fires only when Referer present, only header-safe shapes
    • all-techniques-emitted, payload coverage, technique-list sorted, param-names sorted
    • determinism, baseline-not-mutated (URL / body / Referer)
    • disjoint-from-ssrf-probe (no Type/Class collision)
    • name-match over-inclusive cases (returnUrl, RedirectURI, etc.) + correct negatives
    • header-safe table, known-technique table-consistency
    • multipart body / JSON array / no-Referer → no variants on respective surfaces
  • Full go test ./... green (all existing tests still pass; buildregistry_forbidden_test.go updated for the new buildRegistry arg)
  • --open-redirect flag visible in scan --help

🤖 Generated with Claude Code

Targets the CWE-601 / ASVS V5.1.5 unvalidated-redirect family at the
request-parameter layer. Where --ssrf-probe attacks what server-side
network resource the application fetches on the caller's behalf,
--open-redirect attacks what destination the application bounces the
caller's browser to: a redirect-destination value (next, redirect_uri,
returnTo, callback, ...) is reflected into a Location: header reaching
an attacker-controlled external site, turning the trusted host into a
phishing-redirect surface or leaking OAuth authorization codes via the
redirect_uri.

Four surfaces (query, urlencoded body, top-level JSON string, Referer
header when present) cross-producted with seven disjoint payload
techniques: backslash-host, cross-origin, data-uri, javascript-uri,
protocol-relative, userinfo-confusion, whitespace-prefix. The Referer
surface emits only the header-safe subset (net/http rejects/trims the
other two). Eligibility by name substring match against a
redirect-destination token list, or by value shape (absolute http(s)
URL). Every variant keeps the caller's own credentials (Identity ==
nil); requests with no redirect-destination parameter and no Referer
emit zero variants. Findings are class open-redirect, severity MEDIUM.
Off by default (payloads point browsers at attacker URLs and embed
XSS-via-redirect shapes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bugsyhewitt bugsyhewitt merged commit ec20f11 into main May 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant