开始练习【红日团队】的PHP-Audit-Labs 代码审计 Day15
链接:https://github.com/hongriSec/PHP-Audit-Labs
感兴趣的同学可以去练习练习
预备知识:
内容题目均来自 PHP SECURITY CALENDAR 2017
Day 15 - Sleigh Ride代码如下:
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']);
}
漏洞解析 :
这一关主要考察的是 $_SERVER['PHP_SELF']
引发的一个任意网址跳转漏洞
首先,分析一下程序的运行
$_GET['redirect']
参数,那么就 New 一个 Redirect
对象,同时调用Redirect
类的 startRedirect
方法startRedirect
函数接受一个 GET
类型的 params
参数,然后在 explode()
函数中,将 $_SERVER['PHP_SELF']
得到的值,以/
分割成一个$parts
数组。$baseFile
的值为 $parts
数组的最后一个值$url
的值为 $baseFile?http_build_query($params)
,其中的 http_build_query()
函数就是一个将参数进行URL
编码的一个操作,比如 $params='test=123'
setHeaders
函数,首先解码$url
参数,然后header()
函数直接跳转 $url
$_SERVER['PHP']
存在的问题:
初看这个程序没什么问题,但是PHP自带的
$_SERVER['PHP_SELF']
参数是可以控制的。
其中PHP_SELF
指当前的页面绝对地址.
比如我们的网站:http://www.test.com/redict/index.php
,
那么PHP_SELF
就是/redict/index.php
。但有个小问题很多人没有注意到。
当URL
是PATH_INFO
的时候,比如:http://www.test.com/redict/index.php/admin
,那么PHP_SELF
就是/redict/index.php/admin
也就是说,其实PHP_SELF
有一部分是我们可以控制的。
双编码问题:
URL
本来是被浏览器编码
过一次,服务器接收到来自浏览器URL请求的时候,会将URL解码
一次,由于在程序中我们看到有urldecode()
函数存在,它会再次解码一次URL
,此时双编码URL
就可以利用,用于绕过某些关键词检测。比如将/
(两次编码)为: %252f
漏洞利用:
比如我们要跳转到:baidu.com
,那么就可以构造 Payload :http://10.211.55.5/phpcode/day15/url.php/http:%252f%252fbaidu.com?redirect=test¶ms=test123
,访问即可重定向跳转到https://www.baidu.com/
网址。如下图所示,发生了 302 跳转:
本次实例分析, 我们选取的是
360webscan 的防护脚本一个历史漏洞
。
正是使用了 $_SERVER['PHP_SELF']
这个变量,导致可以绕过360webscan
防护脚本的防护,脚本的防护效果失效,现在此防护脚本更新了。
其结构为:
因为这只是一个防护的辅助脚本,任何的程序都可以安装使用,这里就以 Emlog5.3.1
(百度网盘 密码: hkb4)博客程序为例子,程序不重要,这个脚本可以安装接入到任何的程序中。
因为这只是一个防护的辅助脚本,任何的程序都可以安装使用,这里就以 Emlog5.3.1
博客程序为例子,程序不重要,这个脚本可以安装接入到任何的程序中。安装的方法:解压得到 360safe 文件夹,之后上传到我们的网站根目录中,同时在任意的全局文件中加入如下代码即可安装成功:
if(is_file($_SERVER['DOCUMENT_ROOT'].'/360safe/360webscan.php')){
require_once($_SERVER['DOCUMENT_ROOT'].'/360safe/360webscan.php');
}
在按照上述安装方法安装后,测试访问:http://www.test.com/index.php?test=
,XSS拦截显示:
比如GET
传递的数据存在SQL
注入恶意字符都会被拦截,虽然本脚本的正则过滤规则很好了,但是通过这一个$_SERVER['PHP_SELF']
,可以通过白名单规则绕过攻击防护。
在存在绕过漏洞的360webscan历史版本中,如下 第198-218行
的的代码(拦截目录白名单检测):
function webscan_white($webscan_white_name,$webscan_white_url=array()) {
$url_path=$_SERVER['PHP_SELF'];
$url_var=$_SERVER['QUERY_STRING'];
if (preg_match("/".$webscan_white_name."/is",$url_path)==1&&!empty($webscan_white_name)) {
return false;
}
foreach ($webscan_white_url as $key => $value) {
if(!empty($url_var)&&!empty($value)){
if (stristr($url_path,$key)&&stristr($url_var,$value)) {
return false;
}
}
elseif (empty($url_var)&&empty($value)) {
if (stristr($url_path,$key)) {
return false;
}
}
}
return true;
}
在上图的 第2行
,我们看到 $url_path
的值是直接取的$_server['PHP_SELF']
的值,同时没有做任何的验证或过滤。那么我们只要在请求的URL(提交的参数中)
存在白名单目录,那么就可以绕过安全检测。
因为在 webscan_cache.php
中的默认的白名单目录存在 admin
。
然后我们访问:http://www.test.com/index.php/admin?test=
此处虽然返回的状态码是 404
,但是,我们发现已经不再拦截了,如果再配合某些CMS
或者PHP系统的伪静态特殊性,那么就可以成功的绕过防护。
本次审计的其实不是漏洞,主要是一个 $_SERVER['PHP_SELF']
的问题,再遇上某系伪静态规则配合下,就会导致各种由此形成的各种漏洞。因此,这里推荐使用 $_SERVER['SCRIPT_NAME']
代替即可,同时,我们可以看到在最新的360webscan
中已经更新了这个问题,并且就是使用$_SERVER['SCRIPT_NAME']
。
再次感谢【红日团队】
360webscan防注入脚本全面绕过
[PHP防火墙]输入内容存在危险字符,安全起见,已被本站拦截