CVE-2025-49515
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. Teachers and administrators can hide a course so that it does not appear in course listings while it is being prepared or after it has ended. Only users with the moodle/course:viewhiddencourses capability should see a hidden course's details.
An Information Exposure vulnerability (CVE-2025-49515) has been identified in the course listing code of Moodle and IOMAD, which allows a visitor to read the summary, teachers, and other listing details of a hidden course by requesting it by ID from course/category.ajax.php. On sites that do not force users to log in, no account is needed. Moodle tracks this issue as MSA-25-0033 (MDL-84518).
Per MITRE CWE-200: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
This issue affects all versions of Moodle before 4.1.19, versions 4.2.0 through 4.4.8, versions 4.5.0 through 4.5.4, and version 5.0.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(course/renderer.php) - Affected versions: <4.1.19, >=4.2.0 <4.4.9, >=4.5.0 <4.5.5, >=5.0.0 <5.0.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.19, 4.4.9, 4.5.5, 5.0.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.19, 4.2.0 through 4.4.8, 4.5.0 through 4.5.4, and 5.0.0 of Moodle, and in IOMAD releases built on those versions. Moodle rates it Serious.
The course and category browser loads course details on demand. When a visitor expands a course in a listing, the page requests course/category.ajax.php, which calls require_login() only when the site's forcelogin setting is on, and then hands the request to core_course_renderer::coursecat_ajax(). For a course request, that method read the course ID from the request and rendered the course's details:
} else if ($type === self::COURSECAT_TYPE_COURSE) {
// This is a request for the course information.
$courseid = required_param('courseid', PARAM_INT);
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$chelper = new coursecat_helper();
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
return $this->coursecat_coursebox_content($chelper, $course);The method checked neither the course's visibility nor the caller's capabilities. coursecat_coursebox_content() renders the course summary, overview files, course contacts (usually the teachers), category name, and course custom fields. A request for the ID of a hidden course therefore returned those details even though the course was hidden from every listing.
Course IDs are sequential, so an attacker can walk through them and collect the details of every hidden course. On sites with forcelogin off, the endpoint answers anonymous requests; with it on, any logged-in user can make the same request. The exposure is limited to the descriptive details shown in course listings. It does not give access to the course itself or its activities.
The fix loads the course without MUST_EXIST, so a missing course and a hidden course produce the same invalidcourseid error, and it requires moodle/course:viewhiddencourses in the course context before rendering a hidden course:
$course = $DB->get_record('course', ['id' => $courseid], '*', IGNORE_MISSING);
if ($course === false) {
throw new \moodle_exception('invalidcourseid');
}
$coursecontext = context_course::instance($course->id, MUST_EXIST);
if ($course->visible == 0 && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
throw new \moodle_exception('invalidcourseid');
}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, turning on Force users to log in (forcelogin) under Site administration > Security > Site security settings blocks anonymous requests to the endpoint. Logged-in users can still reach it, so this only narrows the exposure.
Users of the affected components should apply one of the following mitigations:
- Upgrade to Moodle 4.1.19, 4.4.9, 4.5.5, 5.0.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
- Vincent Schneider (finder)