Re: phpmyadmin / CVE-2016-9861 / PMASA-2016-66
Brian May <bam@debian.org> writes:
> I have patched a number of vulnerabilities in phpmyadmin in wheezy;
> there is a version available for testing at
> https://people.debian.org/~bam/debian/pool/main/p/phpmyadmin/
> The included isAllowedDomain does not include some checks that are in
> later versions:
I have now added these checks, and updated the package available at the
above URL. The updated debdiff is below.
diff -Nru phpmyadmin-3.4.11.1/debian/changelog phpmyadmin-3.4.11.1/debian/changelog
--- phpmyadmin-3.4.11.1/debian/changelog 2016-09-15 07:17:16.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/changelog 2016-12-06 08:18:14.000000000 +1100
@@ -1,3 +1,25 @@
+phpmyadmin (4:3.4.11.1-2+deb7u7) wheezy-security; urgency=high
+
+ * Non-maintainer upload by the LTS Team.
+ * CVE-2016-4412 / PMASA-2016-57: A user can be tricked in to following a
+ link leading to phpMyAdmin, which after authentication redirects to
+ another malicious site.
+ * CVE-2016-6626 / PMASA-2016-49: In the fix for PMASA-2016-57, we didn't
+ have sufficient checking and was possible to bypass whitelist.
+ * CVE-2016-9849 / PMASA-2016-60: Username deny rules bypass (AllowRoot &
+ Others) by using Null Byte.
+ * CVE-2016-9850 / PMASA-2016-61: Username matching for the allow/deny rules
+ may result in wrong matches and detection of the username in the rule due
+ to non-constant execution time.
+ * CVE-2016-9861 / PMASA-2016-66: In the fix for PMASA-2016-49, we has buggy
+ checks and was possible to bypass whitelist.
+ * CVE-2016-9864 / PMASA-2016-69: Multiple SQL injection vulnerabilities.
+ * CVE-2016-9865 / PMASA-2016-70: Due to a bug in serialized string parsing,
+ it was possible to bypass the protection offered by PMA_safeUnserialize()
+ function.
+
+ -- Brian May <bam@debian.org> Tue, 06 Dec 2016 08:18:14 +1100
+
phpmyadmin (4:3.4.11.1-2+deb7u6) wheezy-security; urgency=high
* Non-maintainer upload by the Long Term Security Team.
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-49.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-49.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-49.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-49.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,13 @@
+--- a/libraries/core.lib.php
++++ b/libraries/core.lib.php
+@@ -864,6 +864,10 @@
+ function PMA_isAllowedDomain($url)
+ {
+ $arr = parse_url($url);
++ // Avoid URLs without hostname or with credentials
++ if (empty($arr['host']) || ! empty($arr['user']) || ! empty($arr['pass'])) {
++ return false;
++ }
+ $domain = $arr["host"];
+ $domainWhiteList = array(
+ /* Include current domain */
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-57.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-57.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-57.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-57.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,68 @@
+--- a/libraries/core.lib.php
++++ b/libraries/core.lib.php
+@@ -852,4 +852,38 @@
+
+ return unserialize($data);
+ }
++
++/**
++ * Checks whether domain of URL is whitelisted domain or not.
++ * Use only for URLs of external sites.
++ *
++ * @param string $url URL of external site.
++ *
++ * @return boolean.True:if domain of $url is allowed domain, False:otherwise.
++ */
++function PMA_isAllowedDomain($url)
++{
++ $arr = parse_url($url);
++ $domain = $arr["host"];
++ $domainWhiteList = array(
++ /* Include current domain */
++ $_SERVER['SERVER_NAME'],
++ /* phpMyAdmin domains */
++ 'wiki.phpmyadmin.net', 'www.phpmyadmin.net', 'phpmyadmin.net',
++ 'docs.phpmyadmin.net',
++ /* mysql.com domains */
++ 'dev.mysql.com','bugs.mysql.com',
++ /* php.net domains */
++ 'php.net',
++ /* Github domains*/
++ 'github.com','www.github.com',
++ /* Following are doubtful ones. */
++ 'www.primebase.com','pbxt.blogspot.com'
++ );
++ if (in_array(strtolower($domain), $domainWhiteList)) {
++ return true;
++ }
++
++ return false;
++}
+ ?>
+--- a/url.php
++++ b/url.php
+@@ -8,9 +8,22 @@
+ */
+ require_once './libraries/common.inc.php';
+
+-if (! PMA_isValid($_GET['url']) || ! preg_match('/^https?:\/\/[^\n\r]*$/', $_GET['url'])) {
++if (! PMA_isValid($_GET['url'])
++ || ! preg_match('/^https?:\/\/[^\n\r]*$/', $_GET['url'])
++ || ! PMA_isAllowedDomain($_GET['url'])
++) {
+ header('Location: ' . $cfg['PmaAbsoluteUri']);
+ } else {
+- header('Location: ' . $_GET['url']);
++ // JavaScript redirection is necessary. Because if header() is used
++ // then web browser sometimes does not change the HTTP_REFERER
++ // field and so with old URL as Referer, token also goes to
++ // external site.
++ echo "<script type='text/javascript'>
++ window.onload=function(){
++ window.location='" . PMA_escapeJsString($_GET['url']) . "';
++ }
++ </script>";
++ // Display redirecting msg on screen.
++ printf(__('Taking you to %s.'), htmlspecialchars($_GET['url']));
+ }
+ ?>
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-60.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-60.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-60.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-60.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,50 @@
+--- a/libraries/auth/cookie.auth.lib.php
++++ b/libraries/auth/cookie.auth.lib.php
+@@ -432,7 +432,7 @@
+
+ if (! empty($_REQUEST['pma_username'])) {
+ // The user just logged in
+- $GLOBALS['PHP_AUTH_USER'] = $_REQUEST['pma_username'];
++ $GLOBALS['PHP_AUTH_USER'] = PMA_sanitizeMySQLUser($_REQUEST['pma_username']);
+ $GLOBALS['PHP_AUTH_PW'] = empty($_REQUEST['pma_password']) ? '' : $_REQUEST['pma_password'];
+ if ($GLOBALS['cfg']['AllowArbitraryServer'] && isset($_REQUEST['pma_servername'])) {
+ $GLOBALS['pma_auth_server'] = PMA_sanitizeMySQLHost($_REQUEST['pma_servername']);
+--- a/libraries/core.lib.php
++++ b/libraries/core.lib.php
+@@ -766,6 +766,24 @@
+ }
+
+ /**
++ * Sanitizes MySQL username
++ *
++ * * strips part behind null byte
++ *
++ * @param string $name User given username
++ *
++ * @return string
++ */
++function PMA_sanitizeMySQLUser($name)
++{
++ $position = strpos($name, chr(0));
++ if ($position !== false) {
++ return substr($name, 0, $position);
++ }
++ return $name;
++}
++
++/**
+ * Safe unserializer wrapper
+ *
+ * It does not unserialize data containing objects
+--- a/libraries/auth/http.auth.lib.php
++++ b/libraries/auth/http.auth.lib.php
+@@ -155,6 +155,9 @@
+ unset($usr_pass);
+ }
+
++ // sanitize username
++ $PHP_AUTH_USER = PMA_sanitizeMySQLUser($PHP_AUTH_USER);
++
+ // User logged out -> ensure the new username is not the same
+ if (!empty($old_usr)
+ && (isset($PHP_AUTH_USER) && $old_usr == $PHP_AUTH_USER)) {
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-1.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-1.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-1.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-1.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,11 @@
+--- a/libraries/ip_allow_deny.lib.php
++++ b/libraries/ip_allow_deny.lib.php
+@@ -169,7 +169,7 @@
+
+ // check for username
+ if (($rule_data[1] != '%') //wildcarded first
+- && ($rule_data[1] != $username)) {
++ && (! hash_equals($rule_data[1], $username))) {
+ continue;
+ }
+
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-2.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-2.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-2.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-61-2.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,45 @@
+--- a/libraries/auth/cookie.auth.lib.php
++++ b/libraries/auth/cookie.auth.lib.php
+@@ -524,14 +524,14 @@
+
+ // Ensures valid authentication mode, 'only_db', bookmark database and
+ // table names and relation table name are used
+- if ($cfg['Server']['user'] != $GLOBALS['PHP_AUTH_USER']) {
++ if (! hash_equals($cfg['Server']['user'], $GLOBALS['PHP_AUTH_USER'])) {
+ foreach ($cfg['Servers'] as $idx => $current) {
+ if ($current['host'] == $cfg['Server']['host']
+ && $current['port'] == $cfg['Server']['port']
+ && $current['socket'] == $cfg['Server']['socket']
+ && $current['ssl'] == $cfg['Server']['ssl']
+ && $current['connect_type'] == $cfg['Server']['connect_type']
+- && $current['user'] == $GLOBALS['PHP_AUTH_USER']) {
++ && hash_equals($current['user'], $GLOBALS['PHP_AUTH_USER'])) {
+ $GLOBALS['server'] = $idx;
+ $cfg['Server'] = $current;
+ break;
+--- a/libraries/auth/http.auth.lib.php
++++ b/libraries/auth/http.auth.lib.php
+@@ -160,7 +160,7 @@
+
+ // User logged out -> ensure the new username is not the same
+ if (!empty($old_usr)
+- && (isset($PHP_AUTH_USER) && $old_usr == $PHP_AUTH_USER)) {
++ && (isset($PHP_AUTH_USER) && hash_equals($old_usr, $PHP_AUTH_USER))) {
+ $PHP_AUTH_USER = '';
+ // -> delete user's choices that were stored in session
+ session_destroy();
+@@ -195,11 +195,12 @@
+
+ // Ensures valid authentication mode, 'only_db', bookmark database and
+ // table names and relation table name are used
+- if ($cfg['Server']['user'] != $PHP_AUTH_USER) {
++ if (! hash_equals($cfg['Server']['user'], $PHP_AUTH_USER)) {
+ $servers_cnt = count($cfg['Servers']);
+ for ($i = 1; $i <= $servers_cnt; $i++) {
+ if (isset($cfg['Servers'][$i])
+- && ($cfg['Servers'][$i]['host'] == $cfg['Server']['host'] && $cfg['Servers'][$i]['user'] == $PHP_AUTH_USER)) {
++ && $cfg['Servers'][$i]['host'] == $cfg['Server']['host']
++ && hash_equals($cfg['Servers'][$i]['user'], $PHP_AUTH_USER)) {
+ $server = $i;
+ $cfg['Server'] = $cfg['Servers'][$i];
+ break;
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-66.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-66.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-66.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-66.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,31 @@
+--- a/libraries/core.lib.php
++++ b/libraries/core.lib.php
+@@ -882,10 +882,17 @@
+ function PMA_isAllowedDomain($url)
+ {
+ $arr = parse_url($url);
+- // Avoid URLs without hostname or with credentials
+- if (empty($arr['host']) || ! empty($arr['user']) || ! empty($arr['pass'])) {
++ // We need host to be set
++ if (! isset($arr['host']) || strlen($arr['host']) == 0) {
+ return false;
+ }
++ // We do not want these to be present
++ $blocked = array('user', 'pass', 'port');
++ foreach ($blocked as $part) {
++ if (isset($arr[$part]) && strlen($arr[$part]) != 0) {
++ return false;
++ }
++ }
+ $domain = $arr["host"];
+ $domainWhiteList = array(
+ /* Include current domain */
+@@ -902,7 +909,7 @@
+ /* Following are doubtful ones. */
+ 'www.primebase.com','pbxt.blogspot.com'
+ );
+- if (in_array(strtolower($domain), $domainWhiteList)) {
++ if (in_array($domain, $domainWhiteList)) {
+ return true;
+ }
+
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-1.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-1.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-1.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-1.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,13 @@
+--- a/libraries/Tracker.class.php
++++ b/libraries/Tracker.class.php
+@@ -240,8 +240,9 @@
+ static public function getLogComment()
+ {
+ $date = date('Y-m-d H:i:s');
++ $user = preg_replace('/\s+/', ' ', $GLOBALS['cfg']['Server']['user']);
+
+- return "# log " . $date . " " . $GLOBALS['cfg']['Server']['user'] . "\n";
++ return "# log " . $date . " " . $user . "\n";
+ }
+
+ /**
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-2.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-2.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-2.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-69-2.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,20 @@
+--- a/tbl_tracking.php
++++ b/tbl_tracking.php
+@@ -106,13 +106,15 @@
+ if (isset($_REQUEST['report_export']) && $_REQUEST['export_type'] == 'sqldumpfile') {
+ @ini_set('url_rewriter.tags','');
+
+- $dump = "# " . sprintf(__('Tracking report for table `%s`'), htmlspecialchars($_REQUEST['table'])) . "\n" .
++ // Replace all multiple whitespaces by a single space
++ $table = htmlspecialchars(preg_replace('/\s+/', ' ', $_REQUEST['table']));
++ $dump = "# " . sprintf(__('Tracking report for table `%s`'), $table) . "\n" .
+ "# " . date('Y-m-d H:i:s') . "\n";
+ foreach($entries as $entry) {
+ $dump .= $entry['statement'];
+ }
+ //$filename = 'log_' . str_replace(';', '', htmlspecialchars($_REQUEST['table'])) . '.sql';
+- $filename = PMA_sanitize_filename('log_' . $_REQUEST['table'] . '.sql');
++ $filename = PMA_sanitize_filename('log_' . $table . '.sql');
+ header('Content-Type: text/x-sql');
+ header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
diff -Nru phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-70.patch phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-70.patch
--- phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-70.patch 1970-01-01 10:00:00.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/pmasa-2016-70.patch 2016-12-06 08:18:14.000000000 +1100
@@ -0,0 +1,25 @@
+From 5e108a340f3eac6b6c488439343b6c1a7454787c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= <michal@cihar.com>
+Date: Tue, 4 Oct 2016 13:17:07 +0200
+Subject: [PATCH] Correctly parse string length when checking serialized data
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Michal Čihař <michal@cihar.com>
+---
+ libraries/core.lib.php | 2 +-
+ test/libraries/core/PMA_safeUnserialize_test.php | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/libraries/core.lib.php
++++ b/libraries/core.lib.php
+@@ -816,7 +816,7 @@
+ case 's':
+ /* string */
+ // parse sting length
+- $strlen = intval($data[$i + 2]);
++ $strlen = intval(substr($data, $i + 2));
+ // string start
+ $i = strpos($data, ':', $i + 2);
+ if ($i === false) {
diff -Nru phpmyadmin-3.4.11.1/debian/patches/series phpmyadmin-3.4.11.1/debian/patches/series
--- phpmyadmin-3.4.11.1/debian/patches/series 2016-09-15 07:03:37.000000000 +1000
+++ phpmyadmin-3.4.11.1/debian/patches/series 2016-12-06 08:18:14.000000000 +1100
@@ -32,3 +32,12 @@
CVE-2016-6623.patch
CVE-2016-6624.patch
CVE-2016-6622.patch
+pmasa-2016-57.patch
+pmasa-2016-49.patch
+pmasa-2016-60.patch
+pmasa-2016-61-1.patch
+pmasa-2016-61-2.patch
+pmasa-2016-66.patch
+pmasa-2016-69-1.patch
+pmasa-2016-69-2.patch
+pmasa-2016-70.patch
--
Brian May <bam@debian.org>
Reply to: