源代码
<?php
class Redirect {
private $websiteHost = 'www.example.com';
private function setHeaders($url) {
$url = urldecode($url);
header("Location: $url");
}
public function startRedirect($params) {
$parts = explode('/', $_SERVER['PHP_SELF']);
$baseFile = end($parts);
$url = sprintf(
"%s?%s",
$baseFile,
http_build_query($params)
);
$this->setHeaders($url);
}
}
if ($_GET['redirect']) {
(new Redirect())->startRedirect($_GET['params']);
}
?>
漏洞关键
用explode('/',$_SERVER['PHP_SELF'])来截取URL中的文件名
这里有个小技巧。在php文件后面跟上/,会正常执行
返回一个数组array(0=>'',1=>'2.php')
再用end函数,取数组中的最后一位。也就是2.php
这里有一个函数http_build_query,作用如下:
$a=array('abc'=>'test','admin'=>'123');
echo (http_build_query($a));#传入值必须为数组
返回abc=test&admin=123
这边我不传入值,为空,与2.php拼接
urldecode($url)
最后header("Location:$url")
如果是用/判断文件名,并且,php文件后面跟上/会正常执行,那么我们输入
2.php/index.php/http:%252f%252f/1.php
传入值,explode分割后:
Array ( [0] => [1] => 2.php [2] => index.php [3] => http:%2f%2f127.0.0.1%2f1.php )
end处理过后:
http:%2f%2f127.0.0.1%2f1.php
sprintf拼接过后:
http:%2f%2f127.0.0.1%2f1.php?
urldecode后:
http://127.0.0.1/1.php?
最后location:$url,跳转到了http://127.0.0.1/1.php
一开始为什么要输入%252f呢。因为程序会urldecode解码一次,而浏览器又会自动解码一次,如果输入%2f(/),浏览器第一次解码。就会将/传入后端,导致explode,分割,不能成功为http://,传入的是%252f,第一次解码过后,%25被解码成%,剩下%和2f重新组合,成为%2f,最后urldecode,为/