CVE-2016-5100: Use of Insufficiently Random Values (CWE-330)
value: Froxlor before 0.9.35 uses the PHP rand function for random number generation, which makes it easier for remote attackers to guess the password reset token by predicting a value.
CWE description: The product uses insufficiently random numbers or values in a security context that depends on unpredictable numbers.
CVSS v3 base score: 9.8
Published: 2017-02-13T18:59Z
Repository: https://github.com/Froxlor/Froxlor
Language: PHP
Files changed: index.php, lib/functions/system/function.randomStr.php
Commit date: 2016-01-28 08:27:15 +0100
Commit message: avoid rand() if possible as it is not generating cryptographically secure values, thx to Hanno for putting some effort into this

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

Diff: diff --git a/index.php b/index.php
index 61822773..4747c0ce 100644
--- a/index.php
+++ b/index.php
@@ -343,12 +343,12 @@ if ($action == 'forgotpwd') {
 
 			if (($adminchecked && Settings::Get('panel.allow_preset_admin') == '1') || $adminchecked == false) {
 				if ($user !== false) {
 					// build a activation code
 					$timestamp = time();
-					$first = substr(md5($user['loginname'] . $timestamp . rand(0, $timestamp)), 0, 15);
-					$third = substr(md5($user['email'] . $timestamp . rand(0, $timestamp)), -15);
+					$first = substr(md5($user['loginname'] . $timestamp . randomStr(16)), 0, 15);
+					$third = substr(md5($user['email'] . $timestamp . randomStr(16)), -15);
 					$activationcode = $first . $timestamp . $third . substr(md5($third . $timestamp), 0, 10);
 
 					// Drop all existing activation codes for this user
 					$stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_ACTIVATION . "`
 						WHERE `userid` = :userid
diff --git a/lib/functions/system/function.randomStr.php b/lib/functions/system/function.randomStr.php
new file mode 100644
index 00000000..285ca27b
--- /dev/null
+++ b/lib/functions/system/function.randomStr.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * This file is part of the Froxlor project.
+ * Copyright (c) 2010 the Froxlor Team (see authors).
+ *
+ * For the full copyright and license information, please view the COPYING
+ * file that was distributed with this source code. You can also view the
+ * COPYING file online at http://files.froxlor.org/misc/COPYING.txt
+ *
+ * @copyright  (c) the authors
+ * @author     Froxlor team <team@froxlor.org> (2016-)
+ * @license    GPLv2 http://files.froxlor.org/misc/COPYING.txt
+ * @package    Functions
+ *
+ */
+
+/**
+ * Function randomStr
+ *
+ * generate a pseudo-random string of bytes
+ *
+ * @param int $length            
+ *
+ * @return string
+ */
+function randomStr($length)
+{
+    if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
+        return random_bytes($length);
+    } elseif (function_exists('openssl_random_pseudo_bytes')) {
+        return openssl_random_pseudo_bytes($length);
+    } else {
+        $pr_bits = '';
+        $fp = @fopen('/dev/urandom', 'rb');
+        if ($fp !== false) {
+            $pr_bits .= @fread($fp, $length);
+            @fclose($fp);
+        } else {
+            $pr_bits = substr(rand(time()).rand(time()), 0, $length);
+        }
+        return $pr_bits;
+    }
+}

Vulnerable code: 					$first = substr(md5($user['loginname'] . $timestamp . rand(0, $timestamp)), 0, 15);
					$third = substr(md5($user['email'] . $timestamp . rand(0, $timestamp)), -15);
Fixed code: 					$first = substr(md5($user['loginname'] . $timestamp . randomStr(16)), 0, 15);
					$third = substr(md5($user['email'] . $timestamp . randomStr(16)), -15);
<?php

/**
 * This file is part of the Froxlor project.
 * Copyright (c) 2010 the Froxlor Team (see authors).
 *
 * For the full copyright and license information, please view the COPYING
 * file that was distributed with this source code. You can also view the
 * COPYING file online at http://files.froxlor.org/misc/COPYING.txt
 *
 * @copyright  (c) the authors
 * @author     Froxlor team <team@froxlor.org> (2016-)
 * @license    GPLv2 http://files.froxlor.org/misc/COPYING.txt
 * @package    Functions
 *
 */

/**
 * Function randomStr
 *
 * generate a pseudo-random string of bytes
 *
 * @param int $length            
 *
 * @return string
 */
function randomStr($length)
{
    if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
        return random_bytes($length);
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
        return openssl_random_pseudo_bytes($length);
    } else {
        $pr_bits = '';
        $fp = @fopen('/dev/urandom', 'rb');
        if ($fp !== false) {
            $pr_bits .= @fread($fp, $length);
            @fclose($fp);
        } else {
            $pr_bits = substr(rand(time()).rand(time()), 0, $length);
        }
        return $pr_bits;
    }
}