buu[HCTF 2018]WarmUp(代码审计)
题目
访问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\" />";
}
?>
分析
可以看到checkfile里面有4个if,第一个if不能进去进去就false;
后面三个if要进去一个,不然执行结束返回false;
下面的文件包含前的判断条件要三个都为真才能包含
然后通过白名单可以知道有两个文件,我们访问一下hint.php
得到一个flag所在的文件
让我们分析一下代码:
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//这个if不进去很简单
if (in_array($page, $whitelist)) {
return true;
}
//这个if进不去,我们传的$page肯定不符合白名单
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
//这段代码的意思就是 将第一个?之前的内容截取出来,注意不包括这个问号
//而且可以发现的是这里不再是原封不动的$page拿来比较
if (in_array($_page, $whitelist)) {
return true;
}
//拿$_page去比较是否匹配白名单,注意这里是截取过.
//可以向办法进入这个if
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
//先将$page解码再截取
if (in_array($_page, $whitelist)) {
return true;
}
//同样这里可以想办法进入这个if
echo "you can't see it";
return false;
//之前没有进入任何if去返回值就失败了
在这个URL中,http://url/source.php?file=hint.php?../../../../../ffffllllaaaagggg
,包含了两个参数:file
和 ../../../../../ffffllllaaaagggg
。
通常,URL中的参数是用?
来分隔的,而参数之间则用&
符号来分隔。在这个URL中,file
是参数名,hint.php?../../../../../ffffllllaaaagggg
是参数值。
现在让我们来解释一下这个参数值 hint.php?../../../../../ffffllllaaaagggg
的含义:
hint.php
:这部分是文件名,表明了要访问的文件是hint.php
。?
:在URL中,?
符号通常用来分隔参数名和参数值。在这个特定的情况下,?
可能被用来分隔文件名和查询字符串,以向hint.php
传递额外的参数。../../../../../ffffllllaaaagggg
:这部分是查询字符串,它以?
开头。在UNIX和类似系统中,../
用来表示上级目录,因此这个查询字符串看起来是在尝试向hint.php
传递参数ffffllllaaaagggg
,同时在路径中向上跳了多个目录。
总结一下,这个URL中的hint.php?../../../../../ffffllllaaaagggg
可能是尝试访问hint.php
文件,并向它传递了一个查询字符串参数,同时尝试在文件路径中向上跳转多层目录。
需要注意的是,?
符号在URL中通常用来分隔参数名和参数值。在这个特定的情况下,?
后面的内容被认为是查询字符串,它可能被hint.php
用来解析和处理。然而,具体的含义和行为取决于服务器端对URL的处理方式。
此题?应该是用来绕过白名单所加
网上有两种解释,结合起来看更容易理解
1、tips:include函数有这么一个神奇的功能:以字符‘/’分隔(而且不计个数),若是在前面的字符串所代表的文件无法被PHP找到,则PHP会自动包含‘/’后面的文件——注意是最后一个‘/’。
2、如果我们的file变量为source.php?../…/…/…/…/ffffllllaaaagggg,先截取 ? 号前字符串白名单验证也就是 source.php,符合,返回ture,然后服务器访问时访问 source.php? 文件不存在,目录穿越至ffffllllaaaagggg,得到我们要的flag。至于为什么需要四个…/,可以一个个增加多次尝试。(source或hint都可)
方法一
一次编码,进入第三个if
方法二
使用urldecode会出现一个问题,假如我将一个字符使用url编码了两遍然后传入的时候,首先浏览器解码了一遍,接下来才是urlencode解码,所以使用两次urlencode,绕过前面的if,进入第四个if