1. 漏洞概述
不安全的 URL 跳转(Unsafe URL Redirect),也被称为开放重定向(Open Redirect)。当 Web 应用程序接收一个用户可控的输入参数(通常是 URL 或路径),并在没有经过严格验证的情况下,直接将其用于重定向操作时,就会产生此类漏洞。
虽然 URL 跳转本身看似不会直接导致服务器被控制或数据泄露,但它常被黑客用于 钓鱼攻击(Phishing)。
例如:攻击者构造一个链接 http://trusted-bank.com/login?redirect=http://fake-bank.com。受害者看到域名是信任的银行域名,点击后却被跳转到了外观一模一样的钓鱼网站,从而骗取账号密码。此外,它还可能被结合用于绕过 CSRF 防御或进行跨站脚本攻击(XSS,例如跳转到 javascript: 伪协议)。
2. 闯关实操 (Exploitation)
第一步:功能观察与抓包分析
打开关卡页面,页面显示了一段文字并附带了四个超链接。
我们使用浏览器的开发者工具(F12)或者直接将鼠标悬停在链接上观察,发现最后两个链接带有参数传递:
<a href="urlredirect.php?url=unsafere.php">像秋天的风一样的少年 </a><a href="urlredirect.php?url=i">我就是我, 放荡不羁的我 </a>
我们点击最后一个链接,浏览器地址栏变为:http://localhost:8899/vul/urlredirect/urlredirect.php?url=i
页面回显了一段文字:“好的, 希望你能坚持做你自己!”
这说明 url 参数控制着页面的某种行为。
第二步:测试外部域名跳转(外链跳转)
既然参数名是 url,我们尝试将其修改为一个完整的外部域名。
在浏览器地址栏中,将参数修改为百度:http://localhost:8899/vul/urlredirect/urlredirect.php?url=http://www.baidu.com
敲击回车发送请求后,页面成功跳转到了百度首页! 这证明存在任意 URL 跳转漏洞,攻击者可以利用它跳转到任意钓鱼网站。
第三步:测试内部路径跳转(内链跳转)
除了外部域名,我们还可以测试内部路径的跳转。
修改参数为返回上级目录的相对路径:http://localhost:8899/vul/urlredirect/urlredirect.php?url=../../../
发送请求后,发现页面成功跳转回了我们本地服务器的根目录(http://localhost:8899/)。利用成功!
3. 源码分析 (Code Review)
为什么只要修改参数就能实现任意跳转?我们来看一下后端的 PHP 源码:
$html="";
// 判断是否通过 GET 接收到了 url 参数,且不为空
if(isset($_GET['url']) && $_GET['url'] != null){$url = $_GET['url'];
// 如果 url 的值等于 'i',则输出一段写死的话
if($url == 'i'){$html.="<p> 好的, 希望你能坚持做你自己!</p>";}else {// 关键漏洞点:如果不是 'i',则直接将其代入 header() 函数进行跳转
header("location:{$url}");
}
}
漏洞成因一目了然:
程序虽然做了一个简单的 if ($url == 'i') 判断,但对于 else 分支的情况,完全没有任何过滤或白名单校验。它直接将用户传入的 $url 变量拼接到 PHP 的 header("location: ...") 函数中执行。
在 PHP 中,header("location: URL") 用于发送 HTTP 302 重定向状态码。如果传入的 URL 是可控的,就等于把方向盘直接交给了用户,导致了不安全的 URL 跳转。
4. 修复建议
防御不安全 URL 跳转漏洞的核心在于:不要信任用户传入的跳转地址,必须进行严格的校验。
修复方案有以下几种(按推荐程度排序):
- 使用映射机制(最安全,强烈推荐)
不要让用户直接传入完整的 URL,而是传入一个 ID 或标识符。后端维护一个字典或数据库表进行映射。// 修复示例:通过字典映射 $redirect_list = ['1' => 'safe_page1.php', '2' => 'safe_page2.php' ]; $id = $_GET['id']; if(array_key_exists($id, $redirect_list)){header("Location:" . $redirect_list[$id]); } else {echo "非法的跳转请求!";} - 严格的白名单校验
如果业务确实需要传入 URL 参数,必须使用严格的白名单。不要使用容易被绕过的正则(如只匹配是否包含某个字符串),而是解析目标 URL 的 Host,并与白名单列表进行绝对匹配。// 修复示例:验证域名白名单 $url = $_GET['url']; $allowed_hosts = ['www.hzx123.xyz', 'localhost']; // 允许跳转的域名 $parsed_url = parse_url($url); if(isset($parsed_url['host']) && in_array($parsed_url['host'], $allowed_hosts)){header("Location: {$url}"); } else {die("禁止跳转到未授权的外部链接!"); } - 限制为相对路径
如果业务只涉及站内跳转,可以限制输入只能是相对路径(例如必须以/开头,且不包含//或@以防协议绕过)。