File协议小trick

构建test文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$url = $_GET['url'];
$info = parse_url($url);
//假设www.site.com为白名单网站
if($info['host'] != 'www.site.com')
{
    echo '目标网址不合法';
    exit;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$data = curl_exec($ch);
curl_close($ch);
echo $data;

上面这段代码我们知道,主要是进行网址过滤,如果用户输入的网址不是规定的网址,则服务器不会发起请求。
这段代码的目的是,防止用户输入其它恶意数据,造成攻击,比如:

http://www.site.com/test.php?url=http://10.0.0.16 –攻击者尝试进入内网

http://www.site.com/test.php?url=http://www.hack.com –攻击者尝试访问不合法网址

但这里有两个小问题:

  1. parse_url原理
    parse_url只是负责字符串解析,它不保证你的协议真伪,这里我们不用http协议,而使用一个根本不存在的协议abc测试:
1
2
3
$url = 'abc://www.baidu.com/test';
$info = parse_url($url);
var_dump($info);

输出结果为:
array(3) { [“scheme”]=> string(3) “abc” [“host”]=> string(13) “www.baidu.com" [“path”]=> string(11) “/test” }

  1. curl支持的协议很多

curl是基于libcurl实现的,支持的协议非常多。

所以我们可以发现两处问题

  1. 白名单只是检测了host,但没有检测协议
  2. curl除了支持http协议,还支持file协议
    这里给出sample

http://url/test.php?url=file://www.site.com/etc/passwd

phpcurl识别出来这是个file协议,会忽略后面的host www.site.com,直接去读取文件/etc/passwd