CVE-2025-67853
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Moodle is an open source learning management system written in PHP. Schools, universities, and companies use it to run online courses. IOMAD is a multi-tenant distribution of Moodle that lets one installation serve several companies or organizations; it is built on Moodle core and shares the affected code. Accounts created through email-based self-registration must be confirmed through a link sent by email. The core_auth_resend_confirmation_email web service lets a user who has not confirmed their account ask for that email again, and it can be called without being logged in.
A Brute Force vulnerability (CVE-2025-67853) has been identified in the core_auth_resend_confirmation_email web service of Moodle and IOMAD, which allows an unauthenticated attacker to check guessed passwords for confirmed accounts, because the service answered differently when the password was correct. Moodle tracks this issue as MSA-25-0057 (MDL-86326).
Per OWASP: A brute force attack can manifest itself in many different ways, but primarily consists in an attacker configuring predetermined values, making requests to a server using those values, and then analyzing the response. For the sake of efficiency, an attacker may use a dictionary attack (with or without mutations) or a traditional brute-force attack (with given classes of characters e.g.: alphanumeric, special, case (in)sensitive).
This issue affects all versions of Moodle before 4.1.22, versions 4.2.0 through 4.4.11, versions 4.5.0 through 4.5.7, versions 5.0.0 through 5.0.3, and version 5.1.0, including End-of-Life release lines such as Moodle 4.3. IOMAD releases built on these Moodle versions contain the same code and are affected as well.
Details
Module Info
- Product: Moodle, IOMAD
- Affected packages:
moodle/moodle(auth/classes/external.php) - Affected versions: <4.1.22, >=4.2.0 <4.4.12, >=4.5.0 <4.5.8, >=5.0.0 <5.0.4, >=5.1.0 <5.1.1
- GitHub repository: https://github.com/moodle/moodle (Moodle), https://github.com/iomad/iomad (IOMAD)
- Published packages: https://packagist.org/packages/moodle/moodle
- Package manager: Composer
- Fixed in:
- OSS Moodle 4.1.22, 4.4.12, 4.5.8, 5.0.4, 5.1.1
- NES for Moodle v4.3.13
- NES for IOMAD v4.3.4
Vulnerability Info
This High-severity vulnerability is found in the moodle/moodle package in all versions before 4.1.22, 4.2.0 through 4.4.11, 4.5.0 through 4.5.7, 5.0.0 through 5.0.3, and 5.1.0 of Moodle, and in IOMAD releases built on those versions. Moodle rates it Minor.
The service is declared in lib/db/services.php with 'loginrequired' => false and 'ajax' => true, so anyone can call it. core_auth_external::resend_confirmation_email() took a username and password, checked them with authenticate_user_login(), and only then looked at whether the account was already confirmed:
if (is_restored_user($username)) {
throw new moodle_exception('restoredaccountresetpassword', 'webservice');
}
$user = authenticate_user_login($username, $password);
if (empty($user)) {
throw new moodle_exception('invalidlogin');
}
if ($user->confirmed) {
throw new moodle_exception('alreadyconfirmed');
}For a confirmed account, a wrong password produced invalidlogin and a correct password produced alreadyconfirmed. That difference tells the caller whether a guessed password is right, for any confirmed account on the site. The service calls authenticate_user_login() without a login token or reCAPTCHA response, and in that case the function skips both checks, so a site's login reCAPTCHA does not slow these guesses down. Failed guesses do still count toward the account lockout threshold, which is off by default.
An unauthenticated attacker who knows usernames can use the service to test passwords against them, and a correct guess gives the attacker a working password for that account.
The fix looks the account up by username first. A confirmed account is rejected before any password check, and when the site's protectusernames setting is on, the response is the same invalidlogin error that a wrong password gives:
$user = core_user::get_user_by_username($username);
if (!empty($user) && $user->confirmed) {
if (!empty($CFG->protectusernames)) {
throw new moodle_exception('invalidlogin');
}
throw new moodle_exception('alreadyconfirmed');
}Restored accounts that must reset their password get the same treatment. The password is now checked only for accounts that are still unconfirmed.
Mitigation
Moodle publishes security fixes only for its supported release lines. Earlier release lines, including Moodle 4.3 and the IOMAD releases built on it, are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, sites can limit password guessing through this service by setting an account lockout threshold under Site administration > Security > Site security settings, since failed attempts through the service count toward it.
Users of the affected components should apply one of the following mitigations:
- Upgrade to Moodle 4.1.22, 4.4.12, 4.5.8, 5.0.4, 5.1.1, or later, or to an IOMAD release built on one of those versions.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- Petr Skoda (finder)