PhpMyadmin命令执行漏洞(CVE-2016-5734)

PhpMyadmin命令执行漏洞(CVE-2016-5734) | Ryweer's Blog Box
文章目录

漏洞介绍

CVE-2016-5734是PhpMyadmin命令执行漏洞。

影响范围:

PHP版本4.3.0-5.4.6中能触发,PHP 5.4.7后就不能触发。

PhpMyAdmin 4.3.0 - 4.6.2存在该漏洞,PhpMyadmin在4.6.3版本中修复了该漏洞。

漏洞成因分析

这个漏洞主要是由PHP语言中preg_replace()函数造成得。我们先看介绍一下这个函数。

preg_replace()函数得作用是执行一个正则表达式得搜索和替换。其具体参数如下:

1
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

$pattern参数代表要搜索的模式,可以是一个字符串或者字符串数组;

$replacement参数表示用于替换的字符串或字符串数组;

$subject参数表示要搜索替换的目标字符串或字符串数组;

$limit参数可选,表示对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制);

$count参数可选,代表要替换执行的次数。

当subject为一个数组时,preg_replace()返回一个数组,其他情况返回一个字符串。如果匹配被查找到,替换后的 subject 被返回,其他情况下 返回没有改变的 subject。如果发生错误,返回 NULL。

要触发preg_replace()漏洞有两个前提。第一个参数需要e标识符,有了它可以执行第二个参数的命令;第一个参数需要在第三个参数中的中有匹配,不然会返回第三个参数而不执行命令 。

例如:

1
2
3
4
5
echo preg_replace('/test/e', 'phpinfo()', 'just test');

echo preg_replace('/test/e', 'phpinfo()', 'just tesxt');
echo preg_replace('/tesxt/e', 'phpinfo()', 'just test');
//这两种没有匹配上,所以返回值是第三个参数,不能执行命令

接下来我们再正式来分析CVE-2016-5734的产生原理。

漏洞的产生点在TableSearch.class.php中的_getRegexReplaceRows函数中。以下是这个函数的代码。

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
function ($columnIndex, $find, $replaceWith, $charSet)
{
$column = $this->_columnNames[$columnIndex];
$sql_query = "SELECT "
. PMA_Util::backquote($column) . ","
. " 1," // to add an extra column that will have replaced value
. " COUNT(*)"
. " FROM " . PMA_Util::backquote($this->_db)
. "." . PMA_Util::backquote($this->_table)
. " WHERE " . PMA_Util::backquote($column)
. " RLIKE '" . PMA_Util::sqlAddSlashes($find) . "' COLLATE "
. $charSet . "_bin"; // here we
// change the collation of the 2nd operand to a case sensitive
// binary collation to make sure that the comparison is case sensitive
$sql_query .= " GROUP BY " . PMA_Util::backquote($column)
. " ORDER BY " . PMA_Util::backquote($column) . " ASC";

$result = $GLOBALS['dbi']->fetchResult($sql_query, 0);

if (is_array($result)) {
foreach ($result as $index=>$row) {
$result[$index][1] = preg_replace(
"/" . $find . "/",
$replaceWith,
$row[0]
);
}
}
return $result;
}

在上面的函数中使用了preg_replace()函数,并且传入了两个参数$find和$replaceWith。我们来看看这两个参数是从哪里传进来的。首先定位哪里使用了_getRegexReplaceRows()函数,发现在getReplacePreview ()函数中使用了。下面是getReplacePreview ()函数的部分代码。

1
2
3
4
5
6
7
8
9
function getReplacePreview($columnIndex, $find, $replaceWith, $useRegex,
$charSet
) {
$column = $this->_columnNames[$columnIndex];
if ($useRegex) {
$result = $this->_getRegexReplaceRows(
$columnIndex, $find, $replaceWith, $charSet
);
} else {

查看代码发$find和$replaceWihth变量也是从其他地方传过来的,继续往前定位,看哪里调用了getReplacePreview ()函数。然后就到了tbl_find_replace.php文件内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require_once 'libraries/common.inc.php';
require_once 'libraries/TableSearch.class.php';

$response = PMA_Response::getInstance();
$table_search = new PMA_TableSearch($db, $table, "replace");

$connectionCharSet = $GLOBALS['dbi']->fetchValue(
"SHOW VARIABLES LIKE 'character_set_connection'", 0, 1
);
if (isset($_POST['find'])) {
$preview = $table_search->getReplacePreview(
$_POST['columnIndex'],
$_POST['find'],
$_POST['replaceWith'],
$_POST['useRegex'],
$connectionCharSet
);
$response->addJSON('preview', $preview);
exit;
}

可以看到这里用到了getReplacePreview()函数,并且函数参数都是post方法传递进去的,进而产生了漏洞。但是要利用这个漏洞,首先得有数据库查询权限,因此这个漏洞需要在认证了token的情况下才能利用。但是要利用还需要控制传入的三个参数,后两个参数可空,前三个参数为必填项。这三个参数中,前两个是直接post方法传进去的,但第三个不可控,第三个参数代表了要搜索替换的原字符串。因为无法预测第三个参数的值,所以我们需要提前插入一个已知值。也就是先往数据库里插入一个表。这样命令执行漏洞就可以被完整触发了。

流量分析

通过对数据包的捕获,我们可以发现,整个exp执行的过程包括认证、建表、发送paload三个步骤。

认证过程和正常的管理员登陆过程一致,这里就不再赘述了。下面为建表的数据包部分截图。
建表数据包截图

执行exp过程中,若使用者没有添加数据库参数,则脚本自动选择test数据库进行建表,若用户没用指定表名,脚本自动创建prgpwn表,并在该表中添加first列,值为“320F6500”,即“e/0截断”。从这里可以考虑对数据库的操作进行过滤,大部分攻击者攻击过程都会利用到test库,而管理员对test库的使用频率并不高。但是这样可能存在误报,因为不能肯定管理员不对test库进行操作。

下图为发送payload过程的数据流量。

发送Payload数据包截图

这里可以清晰的看到,Payload里使用了system函数,这类危险函数可以直接拦截,并且误报几率很低。

以上为此次对CVE-2016-5734的全部分析报告。