CVE-2025-67856
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. Badges can use a Manual issue by role criterion, which names one or more roles whose holders must award the badge. When a badge names several roles, it can require an award from each of them before the badge is issued.
An Authorization Bypass vulnerability (CVE-2025-67856) has been identified in the badge award page of Moodle and IOMAD, which allows a user who can award badges to issue a manual award under a role they do not hold, satisfying a role criterion that should require someone with that role. Moodle tracks this issue as MSA-25-0060 (MDL-86507).
Per MITRE CWE-863: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.
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(badges/award.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 Medium-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.
badges/award.php requires the moodle/badges:awardbadge capability and then works out which role the awarder is acting as. The role comes from the role URL parameter. When the badge's criterion names more than one role, the page built a list of the accepted roles that the user actually holds, but only used that list to draw the role selector and to check that it was not empty. The submitted role value itself was taken as the issuer role without being compared to that list:
} else {
$pageurl = new moodle_url('/badges/award.php', array('id' => $badgeid));
$issuerrole = new stdClass();
$issuerrole->roleid = $role;Each award is stored by process_manual_award() in badge_manual_award with that issuer role, and the manual criterion is reviewed against those records. A user who holds one of the accepted roles could therefore change the role parameter to another accepted role and record an award as that role. On a badge that requires awards from several roles, one person could supply all of them, and the badge would be issued to recipients who had not been approved by a holder of each required role.
Exploiting this needs moodle/badges:awardbadge, which the manager, teacher, and editing teacher archetypes have by default, plus at least one of the roles named in the badge's criterion. Site administrators may award under any accepted role by design and are not affected by the change. Badges whose criterion names a single role use a separate code path that checks the user holds that role.
The fix checks the submitted role before anything else uses it. Unless the user is a site administrator, the request stops with the wrongrole message ("The role you want to use is not assigned to you.") when the user has no assignment of that role in the badge's context:
if (!empty($role)) {
if (!user_has_role_assignment($USER->id, $role, $context->id) && !$isadmin) {
// User does not have the role passed by the parameter.
echo $OUTPUT->header();
echo $OUTPUT->notification(get_string('wrongrole', 'badges'));
echo $OUTPUT->footer();
die();
}
}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.
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
- Stefan Hanauska (finder)