CVE-2023-3173 (PHP, published 2023-06-09T02:15Z)
Description: value: Improper Restriction of Excessive Authentication Attempts in GitHub repository froxlor/froxlor prior to 2.0.20.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-307 \u2014 Improper Restriction of Excessive Authentication Attempts
The product does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame, making it more susceptible to brute force attacks.
Repository: https://github.com/froxlor/froxlor
Files changed:
index.php
lng/de.lng.php
lng/en.lng.php
templates/Froxlor/login/enter2fa.html.twig
Commit message: add same loginfail restrictions for entering 2fa code as for user/pwd login

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


Vulnerable code:
		'pagetitle' => lng('login.2fa')
		// get user-data
		$table = $_SESSION['uidtable_2fa'];
		$field = $_SESSION['uidfield_2fa'];
		$uid = $_SESSION['uid_2fa'];
		$isadmin = $_SESSION['unfo_2fa'];
			$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
			$sel_stmt = Database::prepare("SELECT * FROM $table WHERE `" . $field . "` = :uid");
			$del_stmt = Database::prepare("UPDATE $table SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
		'showmessage' => '2'

Fixed code:
	$smessage = isset($_GET['showmessage']) ? (int)$_GET['showmessage'] : 0;
	$message = "";
	if ($smessage > 0) {
		$message = lng('error.2fa_wrongcode');
	}
		'pagetitle' => lng('login.2fa'),
		'message' => $message
	// get user-data
	$table = $_SESSION['uidtable_2fa'];
	$field = $_SESSION['uidfield_2fa'];
	$uid = $_SESSION['uid_2fa'];
	$isadmin = $_SESSION['unfo_2fa'];
			$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
			$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
			$del_stmt = Database::prepare("UPDATE " . $table . " SET `data_2fa` = '' WHERE `" . $field . "` = :uid");
	// wrong 2fa code - treat like "wrong password"
	$stmt = Database::prepare("
		UPDATE " . $table . "
		SET `lastlogin_fail`= :lastlogin_fail, `loginfail_count`=`loginfail_count`+1
		WHERE `" . $field . "`= :uid
	");
	Database::pexecute($stmt, [
		"lastlogin_fail" => time(),
		"uid" => $uid
	]);

	// get data for processing further
	$stmt = Database::prepare("
		SELECT `loginname`, `loginfail_count`, `lastlogin_fail` FROM " . $table . "
		WHERE `" . $field . "`= :uid
	");
	$fail_user = Database::pexecute_first($stmt, [
		"uid" => $uid
	]);

	if ($fail_user['loginfail_count'] >= Settings::Get('login.maxloginattempts') && $fail_user['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))) {
		// Log failed login
		$rstlog = FroxlorLogger::getInstanceOf([
			'loginname' => $_SERVER['REMOTE_ADDR']
		]);
		$rstlog->logAction(FroxlorLogger::LOGIN_ACTION, LOG_WARNING, "User '" . $fail_user['loginname'] . "' entered wrong 2fa code too often.");
		unset($fail_user);
		Response::redirectTo('index.php', [
			'showmessage' => '3'
		]);
		exit();
	}
	unset($fail_user);
	// back to form
		'action' => '2fa_entercode',
		'showmessage' => '1'
		'2fa_wrongcode' => 'Der angegebene Code ist nicht korrekt',
		'2fa_wrongcode' => 'The code entered is not valid',
						{% if message is not empty %}
							<div class="alert alert-danger" role="alert">
								<h4 class="alert-heading">{{ lng('error.error') }}</h4>
								<p>{{ message|raw }}</p>
							</div>
						{% endif %}