Vulnerability: Froxlor before 0.9.33.2 with the default configuration/setup might allow remote attackers to obtain the database password by reading /logs/sql-error.log.
Vulnerability ID: CVE-2015-5959
CWE: CWE-200
Severity score: 2.9
Project: https://github.com/Froxlor/Froxlor
Published: 2017-09-06
File: lib/classes/database/class.Database.php
Commit message: replace passwords even before logging, not just for display

Signed-off-by: Michael Kaufmann (d00p) <d00p@froxlor.org>
Commit author: Michael Kaufmann (d00p)

Vulnerable version (before fix):
 private static function getDB() { 	private static function _showerror($error, $showerror = true) { 		global $userinfo, $theme, $linker;  		/** 		 * log to a file, so we can actually ask people for the error 		 * (no one seems to find the stuff in the syslog) private static function _showerror($error, $showerror = true) { 		} 		$sl_file = makeCorrectFile($sl_dir."/sql-error.log"); 		$sqllog = @fopen($sl_file, 'a'); 		@fwrite($sqllog, date('d.m.Y H:i', time())." --- ".str_replace("\n", " ", $error->getMessage())."\n"); 		@fwrite($sqllog, date('d.m.Y H:i', time())." --- DEBUG: \n".$error->getTraceAsString()."\n"); 		@fclose($sqllog);  		/** private static function _showerror($error, $showerror = true) { 		$err_file = makeCorrectFile($sl_dir."/".$errid."_sql-error.log"); 		$errlog = @fopen($err_file, 'w'); 		@fwrite($errlog, "|CODE ".$error->getCode()."\n"); 		@fwrite($errlog, "|MSG ".$error->getMessage()."\n"); 		@fwrite($errlog, "|FILE ".$error->getFile()."\n"); 		@fwrite($errlog, "|LINE ".$error->getLine()."\n"); 		@fwrite($errlog, "|TRACE\n".$error->getTraceAsString()."\n"); 		@fclose($errlog);  		if ($showerror) {  			// include userdata.inc.php 			require FROXLOR_INSTALL_DIR."/lib/userdata.inc.php";  			// fallback 			$theme = 'Sparkle';  			// le format 			if (isset($sql['root_user']) 				&& isset($sql['root_password']) 				&& (!isset($sql_root) || !is_array($sql_root)) 			) { 				$sql_root = array(0 => array('caption' => 'Default', 'host' => $sql['host'], 'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'user' => $sql['root_user'], 'password' => $sql['root_password'])); 			}  			// hide username/password in messages 			$error_message = $error->getMessage(); 			$error_trace = $error->getTraceAsString(); 			// error-message 			$error_message = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_message); 			$error_message = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_message); 			// error-trace 			$error_trace = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_trace); 			$error_trace = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_trace);  			if ($error->getCode() == 2003) { 			    $error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong."; 			    $error_trace = ""; 			}  			// clean up sensitive data 			unset($sql); 			unset($sql_root);

Fixed version (after fix):
 private static function getDB() { 	private static function _showerror($error, $showerror = true) { 		global $userinfo, $theme, $linker;  		// include userdata.inc.php 		require FROXLOR_INSTALL_DIR."/lib/userdata.inc.php";  		// le format 		if (isset($sql['root_user']) 		    && isset($sql['root_password']) 		    && (!isset($sql_root) || !is_array($sql_root)) 		) { 		    $sql_root = array(0 => array('caption' => 'Default', 'host' => $sql['host'], 'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'user' => $sql['root_user'], 'password' => $sql['root_password'])); 		}  		// hide username/password in messages 		$error_message = $error->getMessage(); 		$error_trace = $error->getTraceAsString(); 		// error-message 		$error_message = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_message); 		$error_message = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_message); 		// error-trace 		$error_trace = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_trace); 		$error_trace = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_trace);  		if ($error->getCode() == 2003) { 		    $error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong."; 		    $error_trace = ""; 		}  		/** 		 * log to a file, so we can actually ask people for the error 		 * (no one seems to find the stuff in the syslog) private static function _showerror($error, $showerror = true) { 		} 		$sl_file = makeCorrectFile($sl_dir."/sql-error.log"); 		$sqllog = @fopen($sl_file, 'a'); 		@fwrite($sqllog, date('d.m.Y H:i', time())." --- ".str_replace("\n", " ", $error_message)."\n"); 		@fwrite($sqllog, date('d.m.Y H:i', time())." --- DEBUG: \n".$error_trace."\n"); 		@fclose($sqllog);  		/** private static function _showerror($error, $showerror = true) { 		$err_file = makeCorrectFile($sl_dir."/".$errid."_sql-error.log"); 		$errlog = @fopen($err_file, 'w'); 		@fwrite($errlog, "|CODE ".$error->getCode()."\n"); 		@fwrite($errlog, "|MSG ".$error_message."\n"); 		@fwrite($errlog, "|FILE ".$error->getFile()."\n"); 		@fwrite($errlog, "|LINE ".$error->getLine()."\n"); 		@fwrite($errlog, "|TRACE\n".$error_trace."\n"); 		@fclose($errlog);  		if ($showerror) {  			// fallback 			$theme = 'Sparkle';  			// clean up sensitive data 			unset($sql); 			unset($sql_root);

Patch:
patch: @@ -312,6 +312,32 @@ private static function getDB() {
 	private static function _showerror($error, $showerror = true) {
 		global $userinfo, $theme, $linker;
 
+		// include userdata.inc.php
+		require FROXLOR_INSTALL_DIR."/lib/userdata.inc.php";
+
+		// le format
+		if (isset($sql['root_user'])
+		    && isset($sql['root_password'])
+		    && (!isset($sql_root) || !is_array($sql_root))
+		) {
+		    $sql_root = array(0 => array('caption' => 'Default', 'host' => $sql['host'], 'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'user' => $sql['root_user'], 'password' => $sql['root_password']));
+		}
+
+		// hide username/password in messages
+		$error_message = $error->getMessage();
+		$error_trace = $error->getTraceAsString();
+		// error-message
+		$error_message = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_message);
+		$error_message = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_message);
+		// error-trace
+		$error_trace = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_trace);
+		$error_trace = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_trace);
+
+		if ($error->getCode() == 2003) {
+		    $error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong.";
+		    $error_trace = "";
+		}
+
 		/**
 		 * log to a file, so we can actually ask people for the error
 		 * (no one seems to find the stuff in the syslog)
@@ -322,8 +348,8 @@ private static function _showerror($error, $showerror = true) {
 		}
 		$sl_file = makeCorrectFile($sl_dir."/sql-error.log");
 		$sqllog = @fopen($sl_file, 'a');
-		@fwrite($sqllog, date('d.m.Y H:i', time())." --- ".str_replace("\n", " ", $error->getMessage())."\n");
-		@fwrite($sqllog, date('d.m.Y H:i', time())." --- DEBUG: \n".$error->getTraceAsString()."\n");
+		@fwrite($sqllog, date('d.m.Y H:i', time())." --- ".str_replace("\n", " ", $error_message)."\n");
+		@fwrite($sqllog, date('d.m.Y H:i', time())." --- DEBUG: \n".$error_trace."\n");
 		@fclose($sqllog);
 
 		/**
@@ -333,43 +359,17 @@ private static function _showerror($error, $showerror = true) {
 		$err_file = makeCorrectFile($sl_dir."/".$errid."_sql-error.log");
 		$errlog = @fopen($err_file, 'w');
 		@fwrite($errlog, "|CODE ".$error->getCode()."\n");
-		@fwrite($errlog, "|MSG ".$error->getMessage()."\n");
+		@fwrite($errlog, "|MSG ".$error_message."\n");
 		@fwrite($errlog, "|FILE ".$error->getFile()."\n");
 		@fwrite($errlog, "|LINE ".$error->getLine()."\n");
-		@fwrite($errlog, "|TRACE\n".$error->getTraceAsString()."\n");
+		@fwrite($errlog, "|TRACE\n".$error_trace."\n");
 		@fclose($errlog);
 
 		if ($showerror) {
 
-			// include userdata.inc.php
-			require FROXLOR_INSTALL_DIR."/lib/userdata.inc.php";
-
 			// fallback
 			$theme = 'Sparkle';
 
-			// le format
-			if (isset($sql['root_user'])
-				&& isset($sql['root_password'])
-				&& (!isset($sql_root) || !is_array($sql_root))
-			) {
-				$sql_root = array(0 => array('caption' => 'Default', 'host' => $sql['host'], 'socket' => (isset($sql['socket']) ? $sql['socket'] : null), 'user' => $sql['root_user'], 'password' => $sql['root_password']));
-			}
-
-			// hide username/password in messages
-			$error_message = $error->getMessage();
-			$error_trace = $error->getTraceAsString();
-			// error-message
-			$error_message = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_message);
-			$error_message = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_message);
-			// error-trace
-			$error_trace = str_replace($sql['password'], 'DB_UNPRIV_PWD', $error_trace);
-			$error_trace = str_replace($sql_root[0]['password'], 'DB_ROOT_PWD', $error_trace);
-
-			if ($error->getCode() == 2003) {
-			    $error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong.";
-			    $error_trace = "";
-			}
-
 			// clean up sensitive data
 			unset($sql);
 			unset($sql_root);
path: lib/classes/database/class.Database.php