CVE-2021-4241: Use of Insufficiently Random Values (CWE-330)
value: A vulnerability, which was classified as problematic, was found in phpservermon. Affected is the function setUserLoggedIn 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 bb10a5f3c68527c58073258cb12446782d223bc3. It is recommended to apply a patch to fix this issue. The identifier of this vulnerability is VDB-213744.
CWE description: The product uses insufficiently random numbers or values in a security context that depends on unpredictable numbers.
CVSS v3 base score: 5.3
Published: 2022-11-15T23:15Z
Repository: https://github.com/phpservermon/phpservermon
Language: PHP
Files changed: src/psm/Service/User.php
Commit date: 2021-06-23 22:05:58 +0200
Commit message: SECURITY: Replaced mt_rand with random_bytes

https://huntr.dev/bounties/1-phpservermon/phpservermon/
CWE-1241: Use of Predictable Algorithm in Random Number Generator

Diff: diff --git a/src/psm/Service/User.php b/src/psm/Service/User.php
index bc01db2..92979ed 100644
--- a/src/psm/Service/User.php
+++ b/src/psm/Service/User.php
@@ -196,13 +196,13 @@ class User
      */
     private function loginWithCookieData()
     {
         if (isset($_COOKIE['rememberme'])) {
             // extract data from the cookie
-            list ($user_id, $token, $hash) = explode(':', $_COOKIE['rememberme']);
+            list($user_id, $token, $hash) = explode('_', $_COOKIE['rememberme']);
             // check cookie hash validity
-            if ($hash == hash('sha256', $user_id . ':' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) {
+            if ($hash == hash('sha256', $user_id . '_' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) {
                 // cookie looks good, try to select corresponding user
                 // get real token from database (and all other data)
                 $user = $this->getUser($user_id);
 
                 if (!empty($user) && $token === $user->rememberme_token) {
@@ -319,19 +319,19 @@ class User
      * Create all data needed for remember me cookie connection on client and server side
      */
     protected function newRememberMeCookie()
     {
         // generate 64 char random string and store it in current user data
-        $random_token_string = hash('sha256', mt_rand());
+        $random_token_string = hash('sha256', random_bytes(64));
         $sth = $this->db_connection->prepare('UPDATE ' .
             PSM_DB_PREFIX . 'users SET rememberme_token = :user_rememberme_token WHERE user_id = :user_id');
         $sth->execute(array(':user_rememberme_token' => $random_token_string, ':user_id' => $this->getUserId()));
 
         // generate cookie string that consists of userid, randomstring and combined hash of both
-        $cookie_string_first_part = $this->getUserId() . ':' . $random_token_string;
+        $cookie_string_first_part = $this->getUserId() . '_' . $random_token_string;
         $cookie_string_hash = hash('sha256', $cookie_string_first_part . PSM_LOGIN_COOKIE_SECRET_KEY);
-        $cookie_string = $cookie_string_first_part . ':' . $cookie_string_hash;
+        $cookie_string = $cookie_string_first_part . '_' . $cookie_string_hash;
 
         // set cookie
         setcookie('rememberme', $cookie_string, time() + PSM_LOGIN_COOKIE_RUNTIME, "/", PSM_LOGIN_COOKIE_DOMAIN);
     }
 
@@ -524,14 +524,12 @@ class User
             if (!$this->getUser()) {
                 return false;
             }
 
             $this->user_preferences = array();
-            foreach (
-                $this->db_connection->query('SELECT `key`,`value` FROM `' .
-                PSM_DB_PREFIX . 'users_preferences` WHERE `user_id` = ' . $this->user_id) as $row
-            ) {
+            foreach ($this->db_connection->query('SELECT `key`,`value` FROM `' .
+                PSM_DB_PREFIX . 'users_preferences` WHERE `user_id` = ' . $this->user_id) as $row) {
                 $this->user_preferences[$row['key']] = $row['value'];
             }
         }
         return true;
     }

Vulnerable code:             list ($user_id, $token, $hash) = explode(':', $_COOKIE['rememberme']);
            if ($hash == hash('sha256', $user_id . ':' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) {
        $random_token_string = hash('sha256', mt_rand());
        $cookie_string_first_part = $this->getUserId() . ':' . $random_token_string;
        $cookie_string = $cookie_string_first_part . ':' . $cookie_string_hash;
            foreach (
                $this->db_connection->query('SELECT `key`,`value` FROM `' .
                PSM_DB_PREFIX . 'users_preferences` WHERE `user_id` = ' . $this->user_id) as $row
            ) {
Fixed code:             list($user_id, $token, $hash) = explode('_', $_COOKIE['rememberme']);
            if ($hash == hash('sha256', $user_id . '_' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) {
        $random_token_string = hash('sha256', random_bytes(64));
        $cookie_string_first_part = $this->getUserId() . '_' . $random_token_string;
        $cookie_string = $cookie_string_first_part . '_' . $cookie_string_hash;
            foreach ($this->db_connection->query('SELECT `key`,`value` FROM `' .
                PSM_DB_PREFIX . 'users_preferences` WHERE `user_id` = ' . $this->user_id) as $row) {