打开页面看到一个滑稽表情,F12 发现提示source.php
。
进入该页面,发现是代码审计,代码如下:
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
看到其中$whitelist
数组有两个值分别为source.php
和hint.php
,进入hint.php
页面:
告诉我们 flag 在 ffffllllaaaagggg 中。
回到source.php
代码,首先看下面部分:
if (! empty($_REQUEST['file']) //$_REQUEST['file']值非空
&& is_string($_REQUEST['file']) //$_REQUEST['file']值为字符串
&& emmm::checkFile($_REQUEST['file']) //能够通过checkFile函数校验
) {
include $_REQUEST['file']; //包含$_REQUEST['file']文件
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
//打印滑稽表情
}
一个 if 语句要求传入的file
变量:
- 非空
- 类型为字符串
- 能够通过
checkFile()
函数校验
同时满足以上三个要求即可包含 file 中的文件,否则打印滑稽表情。
回到上面checkFile()
函数:
highlight_file(__FILE__); //打印代码
class emmm //定义emmm类
{
public static function checkFile(&$page)//将传入的参数赋给$page
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//声明$whitelist(白名单)数组
if (! isset($page) || !is_string($page)) {//若$page变量不存在或非字符串
echo "you can't see it";//打印"you can't see it"
return false;//返回false
}
if (in_array($page, $whitelist)) {//若$page变量存在于$whitelist数组中
return true;//返回true
}
$_page = mb_substr(//该代码表示截取$page中'?'前部分,若无则截取整个$page
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);//url解码$page
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
可以看到函数代码中有四个 if 语句
- 第一个 if 语句对变量进行检验,要求
$page
为字符串,否则返回 false - 第二个 if 语句判断
$page
是否存在于$whitelist
数组中,存在则返回 true - 第三个 if 语句判断截取后的
$page
是否存在于$whitelist
数组中,截取$page
中?
前部分,存在则返回 true - 第四个 if 语句判断 URL 解码并截取后的
$page
是否存在于$whitelist中
,存在则返回 true
若以上四个 if 语句均未返回值,则返回 false
有三个 if 语句可以返回 true,第二个语句直接判断$page
,不可用;
第三个语句截取?
前部分,由于?
被后部分被解析为 get 方式提交的参数,也不可利用;
第四个 if 语句中,先进行 URL 解码再截取,因此我们可以将?
经过两次 URL 编码,在服务器端提取参数时解码一次,checkFile()
函数中解码一次,仍会解码为?
,仍可通过第四个 if 语句校验。(?
两次编码值为%253f
),构造 URL:
http://***:***/source.php?file=source.php%253f../ffffllllaaaagggg
无返回值,由于我们不知道 ffffllllaaaagggg 文件的具体位置,只能依次增加../
,最终在http://***:***/source.php?file=source.php%253f../../../../../ffffllllaaaagggg
中成功回显 flag。
该漏洞 CVE 编号为 CVE-2018-12613。
作者:XD_whYe
链接:https://www.jianshu.com/p/36eaa95068ca
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。