环境
BUUCTF上web的第一题,启动靶机,访问链接。
·http://f53eba82-0f2f-4f96-8120-1c4e8a4711c2.node4.buuoj.cn:81/
解题思路
根据题目提示是代码审计。
查看源码,发现有提示source.php。
访问看看,果然发现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\" />";
}
?>
我们的目的就是要利用include函数获取flag,要执行这个函数需要三个条件同时满足。
!empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file']
即file参数不为空且是字符串且在emm类方法检查后返回true。
接着分析这个检查参数的类。
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//白名单,source.php,hint.php
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//这个判断和前两个条件一样(传给类的参数$page需要是有值且为字符串),可忽略
if (in_array($page, $whitelist)) {
return true;
}
//直接对传的参数进行检查是否是白名单中的值,通过这里我们可以看看hint.php的内容。
$_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;
}
}
直接对传的参数进行检查是否是白名单中的值,通过这里我们可以看看hint.php的内容。
if (in_array($page, $whitelist)) {
return true;
}
//直接对传的参数进行检查是否是白名单中的值,通过这里我们可以看看hint.php的内容。
http://f53eba82-0f2f-4f96-8120-1c4e8a4711c2.node4.buuoj.cn:81/source.php?file=hint.php
flag在ffffllllaaaagggg文件中,现在要通过include包含ffffllllaaaagggg文件即可获取flag。
接着分析下面的语句。
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
mb_strpos (haystack ,needle )返回要查找的字符串在别一个字符串中首次出现的位置
haystack:要被检查的字符串。
needle:要搜索的字符串
mb_substr(str,start,length) 函数返回字符串的一部分。
str:必需。从该 string 中提取子字符串。
start:必需。规定在字符串的何处开始。
length:可选。规定要返回的字符串长度。默认是直到字符串的结尾。
如果我们的参数中没有?
则通过这个判断的参数前后没有变化,即如果传入source.php123,则mb_substr($page,0,mb_strpos($page . '?', '?'));
替换后则是
mb_substr("source.php123",0,mb_strpos("source.php123?", '?'));
截取的结果还是source.php123,但是该文件不在白名单中,返回false。
所以我们传的参数中需要有?
,这样截取的文件名才能通过白名单检测,例如source.php?123,截取的文件名为source.php可以通过白名单,返回true,但是include包含的是source.php?123这个文件,我们的目的要包含ffffllllaaaagggg这个文件,所以简单的构造source.php?ffffllllaaaagggg是不能成功的包含的,我们可以通过../
目录穿越来包含文件,当传入source.php?/…/ffffllllaaaagggg时,php会把source.php?当做一个目录来解析,这样我们就可以读取flag文件了。
通过测试payload为:
?file=hint.php?/../../../../ffffllllaaaagggg
接着分析后面的代码:
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
这里需要注意的是$_REQUEST['file']
提取参数是会进行一次url解码。
这里先对参数进行一次url解码,再进行截取,最后白名单检测。所以这里是对参数进行的第二次url解码。
所以我们这里还可以这样编写payload:
?file=hint.php%253F/../../../../ffffllllaaaagggg
%253F是对?
的两次url编码,这样代码才能运行到最后一个if判断处。
总结
php会对source.php?/
看做目录解析,这是本题的重点。
考察的知识点是文件包含漏洞(phpmyadmin 4.8.1任意文件包含)