phpMyAdmin本地文件包含漏洞
文件libraries/gis/pma_gis_factory.php存在文件包含漏洞
函数factory()中对参数$type变小写,然后file_exists存在则调用include_once包含此文件

Github中pma_gis_factory.php漏洞修复代码,添加了PMA_isValid()判断

需要定位参数$type来源,查看函数factory()的调用,phpmyadmin/gis_data_editor.php
// Get data if any posted $gis_data = array(); if (PMA_isValid($_REQUEST['gis_data'], 'array')) { $gis_data = $_REQUEST['gis_data']; } $gis_types = array( 'POINT', 'MULTIPOINT', 'LINESTRING', 'MULTILINESTRING', 'POLYGON', 'MULTIPOLYGON', 'GEOMETRYCOLLECTION' ); // Extract type from the initial call and make sure that it's a valid one. // Extract from field's values if availbale, if not use the column type passed. if (! isset($gis_data['gis_type'])) { if (isset($_REQUEST['type']) && $_REQUEST['type'] != '') { $gis_data['gis_type'] = strtoupper($_REQUEST['type']); } if (isset($_REQUEST['value']) && trim($_REQUEST['value']) != '') { $start = (substr($_REQUEST['value'], 0, 1) == "'") ? 1 : 0; $gis_data['gis_type'] = substr( $_REQUEST['value'], $start, strpos($_REQUEST['value'], "(") - $start ); } if ((! isset($gis_data['gis_type'])) || (! in_array($gis_data['gis_type'], $gis_types)) ) { $gis_data['gis_type'] = $gis_types[0]; } } $geom_type = $gis_data['gis_type']; // Generate parameters from value passed. $gis_obj = PMA_GIS_Factory::factory($geom_type);
从参数传递过程来看,
$type = $gis_data[‘gis_type’]
$gis_data = $_REQUEST[‘gis_data’]
所以需要构造
$type = $_REQUEST[‘gis_data’][‘gis_type’]并拼接到include_once()中
构造页面并进行访问,但是并没有显示phpinfo()

Phpmyadmin本身的防CSRF机制,查看libraries/common.inc.php文件,关于token的处理
$token_mismatch = true; if (PMA_isValid($_REQUEST['token'])) { $token_mismatch = ($_SESSION[' PMA_token '] != $_REQUEST['token']); } if ($token_mismatch) { /** * List of parameters which are allowed from unsafe source */ $allow_list = array( /* needed for direct access, see FAQ 1.34 * also, server needed for cookie login screen (multi-server) */ 'server', 'db', 'table', 'target', 'lang', /* Session ID */ 'phpMyAdmin', /* Cookie preferences */ 'pma_lang', 'pma_collation_connection', /* Possible login form */ 'pma_servername', 'pma_username', 'pma_password', /* Needed to send the correct reply */ 'ajax_request', /* Permit to log out even if there is a token mismatch */ 'old_usr' ); /** * Allow changing themes in test/theme.php */ if (defined('PMA_TEST_THEME')) { $allow_list[] = 'set_theme'; } /** * Require cleanup functions */ include './libraries/cleanup.lib.php'; /** * Do actual cleanup */ PMA_remove_request_vars($allow_list); }
会对 $_SESSION[‘ PMA_token ‘] 以及 $_REQUEST[‘token’]进行比较,如果不相等,则调用libraries/cleanup.lib.php中PMA_remove_request_vars()函数
function PMA_remove_request_vars(&$whitelist) { // do not check only $_REQUEST because it could have been overwritten // and use type casting because the variables could have become // strings $keys = array_keys( array_merge((array)$_REQUEST, (array)$_GET, (array)$_POST, (array)$_COOKIE) ); foreach ($keys as $key) { if (! in_array($key, $whitelist)) { unset($_REQUEST[$key], $_GET[$key], $_POST[$key], $GLOBALS[$key]); } else { // allowed stuff could be compromised so escape it // we require it to be a string if (isset($_REQUEST[$key]) && ! is_string($_REQUEST[$key])) { unset($_REQUEST[$key]); } if (isset($_POST[$key]) && ! is_string($_POST[$key])) { unset($_POST[$key]); } if (isset($_COOKIE[$key]) && ! is_string($_COOKIE[$key])) { unset($_COOKIE[$key]); } if (isset($_GET[$key]) && ! is_string($_GET[$key])) { unset($_GET[$key]); } } } }
将request/post/cookie/get值置零,所以会影响后续的访问操作
在phpmyadmin测试中,token的值可以在url中看到
在网站根目录下创建phpinfo.txt文件,内容为phpinfo()

访问phpmyadmin后台地址,输入用户名/密码 = root/root

记录url和token值

访问存在漏洞的页面gis_data_editor.php,构造参数gist_data[gis_type] = /../../../phpinfo.txt,最后跟%00截断,运行可以看到phpinfo()访问成功。

参考资料