启动靶机后看到这样的界面
我们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\" />";
}
?>
我们先来分析一下代码看看它干了什么
首先定义了一个名为 emmm
的类,类中有一个静态方法 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;
}
首先定义了一个白名单["source"=>"source.php","hint"=>"hint.php"];
第一个判断首先检查 $page
是否被设置且是否为字符串。如果不满足条件,则输出 "you can't see it"
,并返回 false
。
第二个判断是否在白名单中,如果在则返回ture
$_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 . '?', '?')
);
方法使用 mb_substr
和 mb_strpos
对 $page
进行处理,只取 ?
之前的部分,然后再次检查是否在白名单中。
如果仍未通过检查,代码会对 $page
进行 urldecode
解码,然后重复上述步骤,最后检查解码后的结果是否在白名单中。
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
如果所有的检查都为通过则输出you can't see it 返回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\" />";
}
首先检查$_REQUEST['file']是否为空
检测变量的类型是否是字符串。
然后再这里调用emmm
类里面的checkFile(&$page)方法
如果都符合则包含文件如果不符合则打印笑脸
还有在定义百名单时还有一个文件是hint.php我们进行访问
它说flag不在这里它在一个叫ffffllllaaaagggg的文件中
所以根据我们的分析得出我们的payload为
?file=source.php?/../../../../../ffffllllaaaagggg
这个payload满足它的所有要求大部分flag都在根目录,所以这里的../只要够多就行它最多也就到/目录
我看了其他人说的说是到了后端是会把‘?’解析两次所以他们将payload中的'?'替换成为了%253F
但是我试了直接使用'?'也是可以访问的
我认为是它确实解析了两次
我们的浏览器将'?'解析为了%3F,第一次解析是PHP会自动将我们的%3F解码为'?'
之后代码会对 $page
进行 urldecode
解码,虽然这里会进行解码但是我们的'?'已经时解析过的了
所以它也无法进行url解码因为它认为我的payload中已经不存在url编码了
所以直接使用'?'和使用'%253F'是一样的效果
直接使用是浏览器先将'?'编码为'%3F'然后php将'%3F'解码为'?'进入方法后urldecode函数认为我们传入的参数中没有url编码所以我们的payload经过后没有任何变化
使用编码后是浏览器认为'%253F'已经是url编码所以直接发送给后端php进行解码后成为'%3F'之后urldecode解码为'?'