CVE-2021-38725 (PHP, published 2021-09-09T15:15Z)
Description: value: Fuel CMS 1.5.0 has a brute force vulnerability in fuel/modules/fuel/controllers/Login.php
Severity: MEDIUM (CVSS v3 base score: 5.3)
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/daylightstudio/FUEL-CMS
Files changed:
fuel/modules/fuel/controllers/Login.php
Commit message: fix: for issue #581


Vulnerable code:
						// check if they are no longer in the locked out state and reset variables
						if (isset($user_data['failed_login_timer']) AND (time() - $user_data['failed_login_timer']) > (int)$this->fuel->config('seconds_to_unlock'))
						{
							$user_data['failed_login_attempts'] = 0;
							$this->session->unset_userdata('failed_login_timer');
							unset($user_data['failed_login_timer']);
						}
						else
						{
							// add to the number of attempts if it's an invalid login'
							$num_attempts = (!isset($user_data['failed_login_attempts'])) ? 0 : $user_data['failed_login_attempts'] + 1;
							$user_data['failed_login_attempts'] = $num_attempts;
						}
						// check if they should be locked out
						if (isset($user_data['failed_login_attempts']) AND $user_data['failed_login_attempts'] >= (int)$this->fuel->config('num_logins_before_lock') -1)
						{
							$this->fuel_users_model->add_error(lang('error_max_attempts', $this->fuel->config('seconds_to_unlock')));
							$user_data['failed_login_timer'] = time();
							$this->fuel->logs->write(lang('auth_log_account_lockout', $this->input->post('user_name', TRUE), $this->input->ip_address()), 'debug');
						}
						else
						{
							$this->fuel_users_model->add_error(lang('error_invalid_login'));
							$this->fuel->logs->write(lang('auth_log_failed_login', $this->input->post('user_name', TRUE), $this->input->ip_address(), ($user_data['failed_login_attempts'] + 1)), 'debug');
						}
			if ($this->input->post('email'))
					$this->fuel_users_model->add_error(lang('error_invalid_email'));

Fixed code:
						$this->check_login_attempts($user_data, 'username');
		$session_key = $this->fuel->auth->get_session_namespace();

		$user_data = $this->session->userdata($session_key);

			if (isset($user_data['failed_login_timer']) AND (time() - $user_data['failed_login_timer']) < (int)$this->fuel->config('seconds_to_unlock'))
			{
 				$this->fuel_users_model->add_error(lang('error_max_attempts', $this->fuel->config('seconds_to_unlock')));
				$user_data['failed_login_timer'] = time();
			}
			elseif ($this->input->post('email'))
					// reset failed login attempts
					$user_data['failed_login_timer'] = 0;

					$this->check_login_attempts($user_data, 'email');

				$this->session->set_userdata($session_key, $user_data);
	protected function check_login_attempts(&$user_data, $field)
	{
		if (isset($user_data['failed_login_timer']) AND (time() - $user_data['failed_login_timer']) > (int)$this->fuel->config('seconds_to_unlock'))
		{
			$user_data['failed_login_attempts'] = 0;
			$this->session->unset_userdata('failed_login_timer');
			unset($user_data['failed_login_timer']);
		}
		else
		{
			// add to the number of attempts if it's an invalid login'
			$num_attempts = (!isset($user_data['failed_login_attempts'])) ? 0 : $user_data['failed_login_attempts'] + 1;
			$user_data['failed_login_attempts'] = $num_attempts;
		}

		// check if they should be locked out
		if (isset($user_data['failed_login_attempts']) AND $user_data['failed_login_attempts'] >= (int)$this->fuel->config('num_logins_before_lock') -1)
		{
			$this->fuel_users_model->add_error(lang('error_max_attempts', $this->fuel->config('seconds_to_unlock')));
			$user_data['failed_login_timer'] = time();
			$this->fuel->logs->write(lang('auth_log_account_lockout', $this->input->post($field, TRUE), $this->input->ip_address()), 'debug');
		}
		else
		{
			if ($field == 'email')
			{
				$this->fuel_users_model->add_error(lang('error_invalid_email'));
				$this->fuel->logs->write(lang('error_invalid_email', $this->input->post('email', TRUE), $this->input->ip_address(), ($user_data['failed_login_attempts'] + 1)), 'debug');
			}
			else
			{
				$this->fuel_users_model->add_error(lang('error_invalid_login'));
				$this->fuel->logs->write(lang('auth_log_failed_login', $this->input->post('user_name', TRUE), $this->input->ip_address(), ($user_data['failed_login_attempts'] + 1)), 'debug');
			}
		}
	}