0x01
进去以后一个笑脸,哈哈哈
接下来我们查看源码
发现注释部分提示我们有个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\" />";
}
?>
源代码中发现还有一个hint.php,我们看一下这个页面有什么东西没
页面显示如上图,提示我们flag不在这里,而是在这个ffffllllaaaagggg文件当中,结合源码大致内容,我们有了大致思路,就是文件包含执行代码,那接下来我们分析一下源码吧。
0x02源码分析
一、
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\" />";
}
那我们先看看它对传入的参数做了什么
1.判断变量file是否为空
2.判断变量file是否为字符串
3.调用定义的checkFile函数检查变量file
若上述条件都成功满足,那就include变量file,否则返回一个图片
所以接下来我们去看看他这个checkFile函数代码
二、
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;
}
这里判断变量$page是否为空以及是否为字符串,很简单容易理解,不详细阐述,个别函数作用不知道的可以去学习一下php相关的函数
下一个走起
三、
if (in_array($page, $whitelist)) {
return true;
}
判断变量$whitelist中是否有 $page的值,这是in_array()函数的作用
语法
in_array(value,array,type)
参数
value 必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。
如果给定的值 value 存在于数组 array 中则返回 true。如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。
如果没有在数组中找到参数,函数返回 false。
注释:如果 value 参数是字符串,且 type 参数设置为 true,则搜索区分大小写。
四、
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
这段代码有什么用,这段代码是截取变量$page首次出现?之前的值
五、
if (in_array($_page, $whitelist)) {
return true;
}
判断变量$whitelist中是否存在变量 $_page的值
六、
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
这个跟上面那个作用和比较是一样的
0x03出招
既然我们对源码已经分析过了,知道源码对我们传入的参数都干了什么事情,我们要做的就是那些if语句返回true而不是false,那我们接下来就文件包含
payload:
?file=source.php?/../../../../../../ffffllllaaaagggg
这里解释一下,第一个问号是用来传参的,第二个问号是满足截取条件的,有人可能会有疑问,说,你这个源码分析有问题啊,源码中明明对_$page进行了url解码
$_page = urldecode($page);
你为什么不对第二个问号进行url编码呢?
那我来解释一下,其实这里编码不编码都不影响我们的结果,原因在于include
寻找包含文件的顺序先是在当前工作目录的相对的 include_path 下寻找,然后是当前运行脚本所在目录相对的 include_path 下寻找。例如 include_path 是 .,当前工作目录是 /www/,脚本中要 include 一个 include/a.php 并且在该文件中有一句 include “b.php”,则寻找 b.php 的顺序先是 /www/,然后是 /www/include/。如果文件名以 …/ 开始,则只在当前工作目录相对的 include_path 下寻找。
简而言之,如果include中出现了 …/…/…/…/…/…/那么就会忽略/前面的字符串直接进行查找,所以那个?编码不编码在这里不影响结果,只需要让它满足那个if检查语句就行了
当然,你进行编码也是没问题的,我们可以进行两次URL编码,将?变为%253f,然后上传,那么服务器在接受参数时会进行一次url解码,执行checkFile()函数时进行一次url解码,所以我们对?进行了2次URL编码。
执行结果:
0x04总结
本题主要考察的还是源码审计,主要是对php函数的作用和源码的理解,采取绕过姿势进行绕过或者满足判断条件,然后用到了一个include文件包含