对于我这种新人菜鸡来说,如果拿到web题不知道是哪方面的话,一般就是F12查看源代码,抓包,扫描挨个试试看能找到什么线索。在buu上这道题给了提示,是代码审计,打开网址后是一个滑稽,没有代码,既然是代码审计,网页又没有显示代码也没文件,那一般就是审计源代码,查看页面源代码,可以发现一个明显的提示,source.php一般就是存放源代码的文件了,在原网页上包含该文件,即可看到源代码。
审计是我们需要先了解几个函数的作用:
isset(): 判断变量是否声明;
is_string(): 判断变量是否是字符串;
in_array(search,array): 判断array中是否存在search;
mb_substr(): 函数返回字符串的一部分
参考菜鸟教程:https://www.runoob.com/php/func-string-mb_substr.html
mb_strpos(haystack,needle): 在 haystack 中查找needle第一次出现的位置 ;
empty — 检查一个变量是否为空
参考 :https://www.php.net/manual/zh/function.empty.php
_REQUEST[]:具用_POST[] _GET[]的功能,但是_REQUEST[]比较慢。通过post和get方法提交的所有数据都可以通过_REQUEST数组获得
开始代码审计:
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//得到另外一个文件hint.php,包含得到flag的文件名
//isset()判断变量是否声明,is_string()判断变量是否是字符串
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//检测传进来的值是否匹配白名单列表$whitelist 如果有则执行真
if (in_array($page, $whitelist)) {
return true;
}
//过滤问号的函数(如果$page的值有?则从?之前提取字符串)
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
//第二次检测传进来的值是否匹配白名单列表$whitelist
if (in_array($_page, $whitelist)) {
return true;
}
//对$page进行url解码
$_page = urldecode($page);
//第二次过滤问号的函数(如果$page的值有?则从?之前提取字符串)
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
//第三次检测传进来的值是否匹配白名单列表$whitelist
if (in_array($_page, $whitelist)) {
return true;
}
//若以上都没通过,则返回false
echo "you can't see it";
return false;
}
}
//---------------------------------------------------------------
//这里就到了文件包含,需要传入的参数file不为空且是字符串,经过class emmm的检测,即emmm返回真,则包含file
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\" />";
}
?>
第一个if语句 对变量进行检验,要求page为字符串,否则返回false
第二个if语句 判断page是否存在于whitelist数组中,存在则返回true
第三个if语句 判断截取后的page是否存在于whitelist数组中,存在则返回true
截取page中’?'前的部分
第四个if语句 判断url解码并截取后的page是否存在于whitelist中,存在则返回true
若以上四个if语句均未返回值,则返回false
传入的参数file不为空且是字符串,经过class emmm的检测,即emmm返回真,则开始包含file。
这样,我们就可以开始构造file
有三个if语句可以返回true,第二个语句直接判断$page,不可用。
第一种(利用第三个if):
…/source.php?file=hint.php?../…/…/…/…/ffffllllaaaagggg
先经历第一个if,什么也没有返回,因为不满足条件不返回true,满足条件返回false;
第二个if:匹配白名单,失败,不返回true;
截取file 中 “?”之前的内容hint.php,并传入下一个if;
第三个if:
匹配白名单:返回true。
打印flag文件内容
第二种(利用第四个if,对“?”进行两次URL编码(但经过我的实践,编码一次也可以)):
…/source.php?file=source.php%253f…/…/…/…/…/ffffllllaaaagggg
先经历第一个if,什么也没有返回,因为不满足条件不返回true,满足条件返回false;
第二个if:匹配白名单,失败,不返回true;
截取file 中 “?”之前的内容hint.php,并传入下一个if;
第三个if:匹配失败,对参数进行url解码,先进行url解码再截取;
第四个if:因为在服务器端提取参数时解码过一次,故第三次if匹配失败后经过解码仍为’?’,通过第四个if语句校验。(’?‘两次编码值为’%253f’)。
打印flag文件内容
注意:…/ 有多少个并不清楚,应为不知道flag在底几层目录里,需要从没有开始一次加一个去尝试。