出自:HCTF2018
打开源代码,发现存在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); //将提交的参数进行url解码
$_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 not here, and flag in ffffllllaaaagggg
现在补充一下相关函数:
**mb_strpos()**:返回要查找的字符串在别一个字符串中首次出现的位置
// mb_strpos (haystack ,needle )
// haystack:要被检查的字符串。
// needle:要搜索的字符串
**mb_substr()** 函数返回字符串的一部分。
//str 必需。从该 string 中提取子字符串。
//start 必需。规定在字符串的何处开始。
//ength 可选。规定要返回的字符串长度。默认是直到字符串的结尾
request()//可以以get或者post提交参数
include //文件包含
$_page . '?'//将$_page后拼接.
public static function checkFile(&$page)
&& emmm::checkFile($_REQUEST['file'])
上面这代码表示 将request的file值 传入$page,其实就是将file参数传入到page中
&引用传递变量。这是通过在函数内建立一个本地变量并且该变量在呼叫范围内引用了同一个内容来实现的。例如:
function foo (&$var)
{
$var++;
}
$a=5;
foo ($a);
?>
将使 $a 变成 6。这是因为在 foo 函数中变量 $var 指向了和 $a 指向的同一个内容。
审计代码可知,这两个函数是过滤?的
例如$page=abc **mb_strpos()**中
则 abc拼接? abc?
‘?’在abc的3位置,返回值为3
进入mb_substr() 中$page=abc
从0开始,截取三位,则
$page=abc
相对的,如果传入abc?则,经过mb_strpos()仍然返回三,进而进入到
mb_strpos(),截取为abc
代码审计看注释~~~~
审计完之后,发现最后就是绕过checkFile(),利用文件包含执行 ffffllllaaaagggg.php
而绕过则必须checkFile()都返回true,才可执行checkFile(),进而执行文件包含
第一个if:验证是否是字符串,
第二个if,验证是否在白名单之中,
第三个if:结合上面函数看,先将?提取,在进入白名单绕过,这里要说一下:
有两个payload:
payload1:file=source.php%253f/…/…/…/…/…/ffffllllaaaagggg
payload2:file=source.php?/…/…/…/…/…/ffffllllaaaagggg
第一个payload显然可以绕过,第二个payload虽然有?但截取之后仍然存在source.php,都可以绕过第三if校验
第四个if,也是结合前面的观看,先将url解码,在放在白名单里面校验,所以第一个payload,经过两次编码,第一次是传参的时候会将%25解码我%,接着在urldecode($page)解码之后会变成 %3f即’?'号,截取?之前的存在source.php,绕过,而payload2中‘?’解码之后仍然是问号,所以截取?之前的仍然可以绕过
这里要注意的是第四个if截取的是
**$_page,不是和第一个函数mb_strpos()中的
$page**
我认为urldecode()函数中解码的是$page,如果是
$_page,则payload2则不能绕过,因为经过第一次截取之后,如果第一次存在?,则?之后的值都被舍弃,不能执行后面的ffffllllaaaagggg
最后需要上面都返回true,则才可以执行emmm::checkFile($_REQUEST[‘file’]),进行文件包含
否则,将返回false,不 执行下面的文件包含
最后执行文件包含的时候:
file=source.php%253f…/ffffllllaaaagggg失败,由于不能souce.php是否与ffffllllaaaagggg存在同一目录下,所以需要…/寻找路径
最后发现:/…/…/…/…/…/存在
参考文章:
https://www.jianshu.com/p/36eaa95068ca