CVE-2021-4240 (published 2022-11-15T23:15Z)

A vulnerability, which was classified as problematic, was found in phpservermon. This affects the function generatePasswordResetToken of the file src/psm/Service/User.php. The manipulation leads to use of predictable algorithm in random number generator. The exploit has been disclosed to the public and may be used. The name of the patch is 3daa804d5f56c55b3ae13bfac368bb84ec632193. It is recommended to apply a patch to fix this issue. The identifier VDB-213717 was assigned to this vulnerability.

CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-330
cwe_name: Use of Insufficiently Random Values
description: The product uses insufficiently random numbers or values in a security context that depends on unpredictable numbers.
extended_description: When product generates predictable values in a context requiring unpredictability, it may be possible for an attacker to guess the next value that will be generated, and use this guess to impersonate another user or access sensitive information.
         

Fix information:
repo_url: https://github.com/phpservermon/phpservermon
commit_details: repo_url: https://github.com/phpservermon/phpservermon
author: Tim Zandbergen
author_date: 2021-06-23 22:06:34+02:00
msg: SECURITY: Replaced mt_rand with random_bytes

https://huntr.dev/bounties/2-phpservermon/phpservermon/
CWE-1241: Use of Predictable Algorithm in Random Number Generator
repository: repo_url: https://github.com/phpservermon/phpservermon
repo_name: phpservermon/phpservermon
description: PHP Server Monitor
repo_language: JavaScript
owner: phpservermon
file_changes: filename: psmconfig.inc.php
change_type: ModificationType.MODIFY
diff: @@ -30,7 +30,7 @@
 /**
  * Current PSM version
  */
-define('PSM_VERSION', '3.6.0.beta2');
+define('PSM_VERSION', '3.6.0');
 
 /**
  * URL to check for updates. Will not be checked if turned off on config page.

programming_language: PHP
filename: User.php
change_type: ModificationType.MODIFY
diff: @@ -72,7 +72,7 @@ class User
     protected $user_id;
 
     /**
-     *Current user preferences
+     * Current user preferences
      * @var array $user_preferences
      */
     protected $user_preferences;
@@ -237,15 +237,15 @@ class User
         }
 
         $dirauthconfig = psm_get_conf('dirauth_status');
-        
+
         // LDAP auth enabled
         if ($dirauthconfig === '1') {
             $ldaplibpath = realpath(
                 PSM_PATH_SRC . '..' . DIRECTORY_SEPARATOR .
-                'vendor' . DIRECTORY_SEPARATOR .
-                'viharm' . DIRECTORY_SEPARATOR .
-                'psm-ldap-auth' . DIRECTORY_SEPARATOR .
-                'psmldapauth.php'
+                    'vendor' . DIRECTORY_SEPARATOR .
+                    'viharm' . DIRECTORY_SEPARATOR .
+                    'psm-ldap-auth' . DIRECTORY_SEPARATOR .
+                    'psmldapauth.php'
             );
             // If the library is found
             if ($ldaplibpath) {
@@ -260,20 +260,20 @@ class User
 
         // Authenticated
         if ($ldapauthstatus === true) {
-          // Remove password to prevent it from being saved in the DB.
-          // Otherwise, user may still be authenticated if LDAP is disabled later.
-          $user_password = null;
-          @fn_Debug('Authenticated', $user);
+            // Remove password to prevent it from being saved in the DB.
+            // Otherwise, user may still be authenticated if LDAP is disabled later.
+            $user_password = null;
+            @fn_Debug('Authenticated', $user);
         } else {
 
-          // using PHP 5.5's password_verify() function to check if the provided passwords
-          // fits to the hash of that user's password
-          if (!isset($user->user_id)) {
-              password_verify($user_password, 'dummy_call_against_timing');
-              return false;
-          } elseif (!password_verify($user_password, $user->password)) {
-              return false;
-          }
+            // using PHP 5.5's password_verify() function to check if the provided passwords
+            // fits to the hash of that user's password
+            if (!isset($user->user_id)) {
+                password_verify($user_password, 'dummy_call_against_timing');
+                return false;
+            } elseif (!password_verify($user_password, $user->password)) {
+                return false;
+            }
         } // not authenticated
 
         $this->setUserLoggedIn($user->user_id, true);
@@ -390,8 +390,8 @@ class User
         }
         // generate timestamp (to see when exactly the user (or an attacker) requested the password reset mail)
         $temporary_timestamp = time();
-        // generate random hash for email password reset verification (40 char string)
-        $user_password_reset_hash = sha1(uniqid(mt_rand(), true));
+        // generate random hash for email password reset verification (64 char string)
+        $user_password_reset_hash = hash('sha256', uniqid(random_bytes(64), true));
 
         $query_update = $this->db_connection->prepare('UPDATE ' .
             PSM_DB_PREFIX . 'users SET password_reset_hash = :user_password_reset_hash,

programming_language: PHP
filename: Installer.php
change_type: ModificationType.MODIFY
diff: @@ -212,7 +212,7 @@ class Installer
                 `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
                 `user_name` varchar(64) NOT NULL COMMENT 'user''s name, unique',
                 `password` varchar(255) NOT NULL COMMENT 'user''s password in salted and hashed format',
-                `password_reset_hash` char(40) DEFAULT NULL COMMENT 'user''s password reset code',
+                `password_reset_hash` varchar(64) DEFAULT NULL COMMENT 'user''s password reset code',
                 `password_reset_timestamp` bigint(20) DEFAULT NULL COMMENT 'timestamp of the password reset request',
                 `rememberme_token` varchar(64) DEFAULT NULL COMMENT 'user''s remember-me cookie token',
                 `level` tinyint(2) unsigned NOT NULL DEFAULT '20',
@@ -230,7 +230,7 @@ class Installer
                 UNIQUE KEY `unique_username` (`user_name`)
             ) ENGINE=MyISAM DEFAULT CHARSET=utf8;",
             PSM_DB_PREFIX .
-            'users_preferences' => "CREATE TABLE IF NOT EXISTS `" . PSM_DB_PREFIX . "users_preferences` (
+                'users_preferences' => "CREATE TABLE IF NOT EXISTS `" . PSM_DB_PREFIX . "users_preferences` (
                 `user_id` int(11) unsigned NOT NULL,
                 `key` varchar(255) NOT NULL,
                 `value` varchar(255) NOT NULL,
@@ -732,6 +732,7 @@ class Installer
     /**
      * Patch for v3.6.0 release
      * Added support for Discord and webhooks
+     * Password_reset_hash varchar 40 -> 64 to allow for SHA256 hash
      */
     protected function upgrade360()
     {
@@ -758,6 +759,8 @@ class Installer
         $queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "users` (
             `user_name`, `level`, `name`, `email`)
             VALUES ('__PUBLIC__', 30, 'Public page', 'publicpage@psm.psm')";
+        $queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "users`
+            CHANGE `password_reset_hash` `password_reset_hash`  VARCHAR( 64 ) DEFAULT NULL COMMENT 'user''s password reset code';";
         $this->execSQL($queries);
 
         $this->log('Public page is now available. Added user \'__PUBLIC__\'. See documentation for more info.');

programming_language: PHP