CVE-2025-67847
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. Moodle can export a course, including its uploaded files, as a backup file that can be restored into the same site or a different one.
A Remote Code Execution (RCE) vulnerability (CVE-2025-67847) has been identified in the file handling code of Moodle and IOMAD, which allows a user who can restore course content to plant serialized PHP objects in file records and have Moodle instantiate them while it processes draft files, potentially leading to code execution on the server. Moodle tracks this issue as MSA-25-0051 (MDL-87353).
Per OWASP: Data which is untrusted cannot be trusted to be well formed. Malformed data or unexpected data could be used to abuse application logic, deny service, or execute arbitrary code, when deserialized.
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(lib/filelib.php,repository/draftfiles_ajax.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 Serious.
Every stored file in Moodle has a source field. For files in a user's draft area, Moodle stores a PHP-serialized stdClass there that records the file's original source and, for references, where the file came from. Course backups carry each file's source value, and the restore process writes it back into the file record on the target site, so the person who builds the backup controls that string.
The core file API in lib/filelib.php read the value back with PHP's native unserialize(), with no class restriction and with the @ operator hiding any warnings. This happened in file_get_drafarea_files(), file_restore_source_field_from_draft_file(), file_overwrite_existing_draftfile(), and in several places in file_save_draft_area_files(), which runs whenever a form with a file area is saved:
foreach ($newhashes as $file) {
$file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
if ($source = @unserialize($file->get_source() ?? '')) {
// Field files.source for draftarea files contains serialised object with source and original information.
// We only store the source part of it for non-draft file area.
$file_record['source'] = $source->source;
}The getreferences action of repository/draftfiles_ajax.php, which the file manager calls to list where a file is used, did the same:
$file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, $filename);
if (!$file) {
echo json_encode(false);
} else {
$source = unserialize($file->get_source());Because unserialize() is called without an allowed_classes option, any class named in the string is instantiated, and that class's magic methods such as __wakeup() and __destruct() run when the object is created and discarded. If a serialized object of an arbitrary class reaches the source field of a file that these functions process, Moodle creates it. Combined with a suitable gadget chain from classes available in Moodle core, its plugins, or its bundled libraries, this PHP object injection can lead to code execution as the web server user.
Moodle's advisory describes the attack as starting from restore. The moodle/restore:restorecourse and moodle/restore:restoreactivity capabilities are granted by default to the Manager and Teacher roles in their courses, so on a typical site a teacher can reach this code. A backup file from an untrusted source restored by an administrator can carry the same payload. Users who cannot restore content cannot plant the payload this way.
The fix replaces each call with Moodle's unserialize_object() helper, which only allows stdClass and turns any other class into a plain stdClass, and adds isset() checks before the source property is read:
if ($source = unserialize_object($file->get_source() ?? '')) {
// Field files.source for draftarea files contains serialised object with source and original information.
// We only store the source part of it for non-draft file area.
if (isset($source->source)) {
$file_record['source'] = $source->source;
}
}The same change also switches the cache reader in curl_cache::get() from unserialize() to unserialize_array(). A later advisory, CVE-2026-26045 (MSA-26-0001), applied the same treatment to the remaining call sites in repository/lib.php.
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, restrict the moodle/restore:restorecourse, moodle/restore:restoresection, and moodle/restore:restoreactivity capabilities to trusted roles, and do not restore backup files from untrusted sources.
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
- Dinhnhi from VNPT-VCI (finder)