Merge branch 'QA_4_9-security' into QA_4_9

This commit is contained in:
Isaac Bennetch 2020-03-20 20:31:11 -04:00
commit 10c9709f7d
10 changed files with 65 additions and 38 deletions

View File

@ -1,6 +1,12 @@
phpMyAdmin - ChangeLog
======================
4.9.5 (2020-03-20)
- issue [security] Fix SQL injection with certain usernames (PMASA-2020-2)
- issue [security] Fix SQL injection in particular search situations (PMASA-2020-3)
- issue [security] Fix SQL injection and XSS flaw (PMASA-2020-4)
- issue Deprecate "options" for the external transformation; options must now be hard-coded along with the program name directly in the file.
4.9.4 (2020-01-07)
- issue #15724 Fix 2FA was disabled by a bug
- issue [security] Fix SQL injection vulnerability on the user accounts page (PMASA-2020-1)

View File

@ -420,8 +420,8 @@ class TableSearchController extends TableController
public function getDataRowAction()
{
$extra_data = array();
$row_info_query = 'SELECT * FROM `' . $_POST['db'] . '`.`'
. $_POST['table'] . '` WHERE ' . $_POST['where_clause'];
$row_info_query = 'SELECT * FROM ' . Util::backquote($_POST['db']) . '.'
. Util::backquote($_POST['table']) . ' WHERE ' . $_POST['where_clause'];
$result = $this->dbi->query(
$row_info_query . ";",
DatabaseInterface::CONNECT_USER,

View File

@ -5163,6 +5163,8 @@ class Results
if (count($url_params) > 0
&& (!empty($tmpdb) && !empty($meta->orgtable))
) {
$url_params['where_clause_sign'] = Core::signSqlQuery($url_params['where_clause']);
$result = '<a href="tbl_get_field.php'
. Url::getCommon($url_params)
. '" class="disableAjax">'

View File

@ -467,7 +467,7 @@ class Error extends Message
public function isUserError()
{
return $this->hide_location ||
($this->getNumber() & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE));
($this->getNumber() & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE | E_USER_DEPRECATED));
}
/**

View File

@ -166,12 +166,12 @@ class ErrorHandler
*/
if (error_reporting() == 0 &&
$this->error_reporting != 0 &&
($errno & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE)) == 0
($errno & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE | E_USER_DEPRECATED)) == 0
) {
return;
}
} else {
if (($errno & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE)) == 0) {
if (($errno & (E_USER_WARNING | E_USER_ERROR | E_USER_NOTICE | E_USER_DEPRECATED)) == 0) {
return;
}
}
@ -229,6 +229,7 @@ class ErrorHandler
case E_USER_NOTICE:
case E_USER_WARNING:
case E_USER_ERROR:
case E_USER_DEPRECATED:
// just collect the error
// display is called from outside
break;

View File

@ -29,10 +29,10 @@ abstract class ExternalTransformationsPlugin extends TransformationsPlugin
. ' data via standard input. Returns the standard output of the'
. ' application. The default is Tidy, to pretty-print HTML code.'
. ' For security reasons, you have to manually edit the file'
. ' libraries/classes/Plugins/Transformations/Output/Text_Plain_External'
. '.php and list the tools you want to make available.'
. ' libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php'
. ' and list the tools you want to make available.'
. ' The first option is then the number of the program you want to'
. ' use and the second option is the parameters for the program.'
. ' use. The second option should be blank for historical reasons.'
. ' The third option, if set to 1, will convert the output using'
. ' htmlspecialchars() (Default 1). The fourth option, if set to 1,'
. ' will prevent wrapping and ensure that the output appears all on'
@ -108,6 +108,16 @@ abstract class ExternalTransformationsPlugin extends TransformationsPlugin
$program = $allowed_programs[0];
}
if (isset($options[1]) && strlen((string) $options[1]) > 0) {
trigger_error(sprintf(
__(
'You are using the external transformation command line options field, which has been deprecated for security reasons. '
. 'Add all command line options directly to the definition in %s.'
),
'[code]libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php[/code]'
), E_USER_DEPRECATED);
}
// needs PHP >= 4.3.0
$newstring = '';
$descriptorspec = array(

View File

@ -1812,8 +1812,11 @@ class Privileges
&& $mode == 'change'
) {
$row = $GLOBALS['dbi']->fetchSingleRow(
'SELECT `plugin` FROM `mysql`.`user` WHERE '
. '`User` = "' . $username . '" AND `Host` = "' . $hostname . '" LIMIT 1'
'SELECT `plugin` FROM `mysql`.`user` WHERE `User` = "'
. $GLOBALS['dbi']->escapeString($username)
. '" AND `Host` = "'
. $GLOBALS['dbi']->escapeString($hostname)
. '" LIMIT 1'
);
// Table 'mysql'.'user' may not exist for some previous
// versions of MySQL - in that case consider fallback value
@ -1824,8 +1827,11 @@ class Privileges
list($username, $hostname) = $GLOBALS['dbi']->getCurrentUserAndHost();
$row = $GLOBALS['dbi']->fetchSingleRow(
'SELECT `plugin` FROM `mysql`.`user` WHERE '
. '`User` = "' . $username . '" AND `Host` = "' . $hostname . '"'
'SELECT `plugin` FROM `mysql`.`user` WHERE `User` = "'
. $GLOBALS['dbi']->escapeString($username)
. '" AND `Host` = "'
. $GLOBALS['dbi']->escapeString($hostname)
. '"'
);
if (isset($row) && $row && ! empty($row['plugin'])) {
$authentication_plugin = $row['plugin'];
@ -1965,8 +1971,8 @@ class Privileges
. " `authentication_string` = '" . $hashedPassword
. "', `Password` = '', "
. " `plugin` = '" . $authentication_plugin . "'"
. " WHERE `User` = '" . $username . "' AND Host = '"
. $hostname . "';";
. " WHERE `User` = '" . $GLOBALS['dbi']->escapeString($username)
. "' AND Host = '" . $GLOBALS['dbi']->escapeString($hostname) . "';";
} else {
// USE 'SET PASSWORD ...' syntax for rest of the versions
// Backup the old value, to be reset later
@ -1976,8 +1982,8 @@ class Privileges
$orig_value = $row['@@old_passwords'];
$update_plugin_query = "UPDATE `mysql`.`user` SET"
. " `plugin` = '" . $authentication_plugin . "'"
. " WHERE `User` = '" . $username . "' AND Host = '"
. $hostname . "';";
. " WHERE `User` = '" . $GLOBALS['dbi']->escapeString($username)
. "' AND Host = '" . $GLOBALS['dbi']->escapeString($hostname) . "';";
// Update the plugin for the user
if (!($GLOBALS['dbi']->tryQuery($update_plugin_query))) {

View File

@ -114,7 +114,8 @@ class UserPassword
if ($serverType == 'MySQL'
&& $serverVersion >= 50706
) {
$sql_query = 'ALTER USER \'' . $username . '\'@\'' . $hostname
$sql_query = 'ALTER USER \'' . $GLOBALS['dbi']->escapeString($username)
. '\'@\'' . $GLOBALS['dbi']->escapeString($hostname)
. '\' IDENTIFIED WITH ' . $orig_auth_plugin . ' BY '
. (($password == '') ? '\'\'' : '\'***\'');
} elseif (($serverType == 'MySQL'
@ -182,7 +183,8 @@ class UserPassword
$serverVersion = $GLOBALS['dbi']->getVersion();
if ($serverType == 'MySQL' && $serverVersion >= 50706) {
$local_query = 'ALTER USER \'' . $username . '\'@\'' . $hostname . '\''
$local_query = 'ALTER USER \'' . $GLOBALS['dbi']->escapeString($username)
. '\'@\'' . $GLOBALS['dbi']->escapeString($hostname) . '\''
. ' IDENTIFIED with ' . $orig_auth_plugin . ' BY '
. (($password == '')
? '\'\''
@ -208,8 +210,8 @@ class UserPassword
. " `authentication_string` = '" . $hashedPassword
. "', `Password` = '', "
. " `plugin` = '" . $orig_auth_plugin . "'"
. " WHERE `User` = '" . $username . "' AND Host = '"
. $hostname . "';";
. " WHERE `User` = '" . $GLOBALS['dbi']->escapeString($username)
. "' AND Host = '" . $GLOBALS['dbi']->escapeString($hostname) . "';";
} else {
$local_query = 'SET password = ' . (($password == '')
? '\'\''

View File

@ -38,6 +38,15 @@ if (!$GLOBALS['dbi']->getColumns($db, $table)) {
PhpMyAdmin\Util::mysqlDie(__('Invalid table name'));
}
if (! isset($_GET['where_clause'])
|| ! isset($_GET['where_clause_sign'])
|| ! Core::checkSqlQuerySignature($_GET['where_clause'], $_GET['where_clause_sign'])
) {
/* l10n: In case a SQL query did not pass a security check */
Core::fatalError(__('There is an issue with your request.'));
exit;
}
/* Grab data */
$sql = 'SELECT ' . PhpMyAdmin\Util::backquote($_GET['transform_key'])
. ' FROM ' . PhpMyAdmin\Util::backquote($table)

View File

@ -46,6 +46,7 @@ class ResultsTest extends PmaTestCase
$GLOBALS['PMA_Config'] = new Config();
$GLOBALS['PMA_Config']->enableBc();
$GLOBALS['text_dir'] = 'ltr';
$_SESSION[' HMAC_secret '] = 'test';
$dbi = $this->getMockBuilder('PhpMyAdmin\DatabaseInterface')
->disableOriginalConstructor()
@ -1323,7 +1324,7 @@ class ResultsTest extends PmaTestCase
$meta = new stdClass();
$meta->type = 'BLOB';
$meta->orgtable = 'bar';
$url_params = array('db' => 'foo', 'table' => 'bar');
$url_params = array('db' => 'foo', 'table' => 'bar', 'where_clause' => 'where_clause');
return array(
array(
@ -1337,9 +1338,7 @@ class ResultsTest extends PmaTestCase
$meta,
$url_params,
null,
'<a href="tbl_get_field.php?db=foo&amp;table=bar&amp;server=0'
. '&amp;lang=en'
. '" class="disableAjax">1001</a>'
'class="disableAjax">1001</a>'
),
array(
true,
@ -1352,9 +1351,7 @@ class ResultsTest extends PmaTestCase
$meta,
$url_params,
null,
'<a href="tbl_get_field.php?db=foo&amp;table=bar&amp;server=0'
. '&amp;lang=en'
. '" class="disableAjax">0x123456</a>'
'class="disableAjax">0x123456</a>'
),
array(
true,
@ -1367,9 +1364,7 @@ class ResultsTest extends PmaTestCase
$meta,
$url_params,
null,
'<a href="tbl_get_field.php?db=foo&amp;table=bar&amp;server=0'
. '&amp;lang=en'
. '" class="disableAjax">[BLOB - 4 B]</a>'
'class="disableAjax">[BLOB - 4 B]</a>'
),
array(
false,
@ -1431,7 +1426,7 @@ class ResultsTest extends PmaTestCase
$_SESSION['tmpval']['display_binary'] = $display_binary;
$_SESSION['tmpval']['display_blob'] = $display_blob;
$GLOBALS['cfg']['LimitChars'] = 50;
$this->assertEquals(
$this->assertContains(
$output,
$this->_callPrivateFunction(
'_handleNonPrintableContents',
@ -1471,7 +1466,7 @@ class ResultsTest extends PmaTestCase
$meta2->decimals = 0;
$meta2->name = 'varchar';
$meta2->orgname = 'varchar';
$url_params = array('db' => 'foo', 'table' => 'tbl');
$url_params = array('db' => 'foo', 'table' => 'tbl', 'where_clause' => 'where_clause');
return array(
array(
@ -1489,11 +1484,7 @@ class ResultsTest extends PmaTestCase
array(),
0,
'binary',
'<td class="left hex">' . PHP_EOL
. ' <a href="tbl_get_field.php?'
. 'db=foo&amp;table=tbl&amp;server=0&amp;lang=en'
. '" '
. 'class="disableAjax">[BLOB - 4 B]</a>' . PHP_EOL
'class="disableAjax">[BLOB - 4 B]</a>' . PHP_EOL
. '</td>' . PHP_EOL
),
array(
@ -1596,7 +1587,7 @@ class ResultsTest extends PmaTestCase
$_SESSION['tmpval']['relational_display'] = false;
$GLOBALS['cfg']['LimitChars'] = 50;
$GLOBALS['cfg']['ProtectBinary'] = $protectBinary;
$this->assertEquals(
$this->assertContains(
$output,
$this->_callPrivateFunction(
'_getDataCellForNonNumericColumns',