Moodle: moodle: improper validation in file restore functionality leading to remote code execution
Description
A flaw was identified in Moodle’s backup restore functionality where specially crafted backup files were not properly validated during processing. If a malicious backup file is restored, it could lead to unintended execution of server-side code. Since restore capabilities are typically available to privileged users, exploitation requires authenticated access. Successful exploitation could result in full compromise of the Moodle server.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Moodle backup restore flaw allows authenticated users to execute arbitrary server-side code via crafted backup files due to unsafe deserialization.
Vulnerability
Description
A flaw was identified in Moodle's backup restore functionality where specially crafted backup files were not properly validated during processing. The root cause is unsafe deserialization of file source data when restoring backups, as indicated by a commit replacing @unserialize() with a safer unserialize_object() function [3]. This allows an attacker to inject malicious serialized objects that can trigger code execution upon deserialization.
Exploitation
The restore capability is typically limited to privileged users, such as administrators or managers, meaning exploitation requires authenticated access with restore privileges [1][2]. An attacker with such access can upload a malicious backup file containing crafted serialized data. The vulnerability is triggered during the restore process when Moodle attempts to unserialize the file's source information without proper sanitization.
Impact
Successful exploitation could result in full compromise of the Moodle server, including arbitrary code execution, data exfiltration, and further lateral movement within the network [2]. Given Moodle's widespread use in educational institutions, the impact can be severe, potentially affecting many users and sensitive data.
Mitigation
The Moodle project has released a fix in a commit that addresses the unsafe unserialization by using a dedicated function [3]. Administrators are strongly advised to update to the latest patched version of Moodle. There is no evidence of exploitation in the wild at the time of publication, but given the severity, immediate patching is recommended.
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
moodle/moodlePackagist | >= 5.1.0-beta, < 5.1.2 | 5.1.2 |
moodle/moodlePackagist | >= 5.0.0-beta, < 5.0.5 | 5.0.5 |
moodle/moodlePackagist | < 4.5.9 | 4.5.9 |
Affected products
1Patches
1566054ba11f6MDL-87612 repository: safer unserializing of file source data.
2 files changed · +14 −10
public/repository/coursefiles/lib.php+2 −3 modified@@ -146,12 +146,11 @@ public function get_listing($encodedpath = '', $page = '') { } public function get_link($encoded) { - $info = array(); - $browser = get_file_browser(); // the final file - $params = unserialize(base64_decode($encoded)); + $params = (array) unserialize_array(base64_decode($encoded)); + $contextid = clean_param($params['contextid'], PARAM_INT); $fileitemid = clean_param($params['itemid'], PARAM_INT); $filename = clean_param($params['filename'], PARAM_FILE);
public/repository/lib.php+12 −7 modified@@ -2503,7 +2503,7 @@ public static function overwrite_existing_draftfile($itemid, $filepath, $filenam if ($file = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $filepath, $filename)) { if ($tempfile = $fs->get_file($user_context->id, 'user', 'draft', $itemid, $newfilepath, $newfilename)) { // Remember original file source field. - $source = @unserialize($file->get_source()); + $source = unserialize_object($file->get_source()); // Remember the original sortorder. $sortorder = $file->get_sortorder(); if ($tempfile->is_external_file()) { @@ -2518,9 +2518,7 @@ public static function overwrite_existing_draftfile($itemid, $filepath, $filenam $newfile = $fs->create_file_from_storedfile(array('filepath'=>$filepath, 'filename'=>$filename), $tempfile); // Preserve original file location (stored in source field) for handling references if (isset($source->original)) { - if (!($newfilesource = @unserialize($newfile->get_source()))) { - $newfilesource = new stdClass(); - } + $newfilesource = unserialize_object($newfile->get_source()); $newfilesource->original = $source->original; $newfile->set_source(serialize($newfilesource)); } @@ -2569,10 +2567,14 @@ public static function update_draftfile($draftid, $filepath, $filename, $updated if ($fs->file_exists($usercontext->id, 'user', 'draft', $draftid, $updatedata['filepath'], $updatedata['filename'])) { throw new moodle_exception('fileexists', 'repository'); } - if (($filesource = @unserialize($file->get_source())) && isset($filesource->original)) { + + // Unset original so the references are not shown any more. + $filesource = unserialize_object($file->get_source()); + if (isset($filesource->original)) { unset($filesource->original); $file->set_source(serialize($filesource)); } + $file->rename($updatedata['filepath'], $updatedata['filename']); // timemodified is updated only when file is renamed and not updated when file is moved. $filemodified = $filemodified || ($updatedata['filename'] !== $filename); @@ -2615,11 +2617,14 @@ public static function update_draftfile($draftid, $filepath, $filename, $updated foreach ($files as $f) { if (preg_match("|^$xfilepath|", $f->get_filepath())) { $path = preg_replace("|^$xfilepath|", $updatedata['filepath'], $f->get_filepath()); - if (($filesource = @unserialize($f->get_source())) && isset($filesource->original)) { - // unset original so the references are not shown any more + + // Unset original so the references are not shown any more. + $filesource = unserialize_object($f->get_source()); + if (isset($filesource->original)) { unset($filesource->original); $f->set_source(serialize($filesource)); } + $f->rename($path, $f->get_filename()); if ($filemodified && $f->get_filepath() === $updatedata['filepath'] && $f->get_filename() === $filename) { $f->set_timemodified(time());
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-ggxq-2mg9-8966ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-26045ghsaADVISORY
- access.redhat.com/security/cve/CVE-2026-26045ghsavdb-entryx_refsource_REDHATWEB
- bugzilla.redhat.com/show_bug.cgighsaissue-trackingx_refsource_REDHATWEB
- github.com/moodle/moodle/commit/566054ba11f609a6d48d09b32e85d435d49927daghsaWEB
- moodle.org/mod/forum/discuss.phpghsaWEB
News mentions
0No linked articles in our index yet.