VYPR
High severityNVD Advisory· Published Feb 21, 2026· Updated Feb 26, 2026

Moodle: moodle: improper validation in file restore functionality leading to remote code execution

CVE-2026-26045

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.

PackageAffected versionsPatched versions
moodle/moodlePackagist
>= 5.1.0-beta, < 5.1.25.1.2
moodle/moodlePackagist
>= 5.0.0-beta, < 5.0.55.0.5
moodle/moodlePackagist
< 4.5.94.5.9

Affected products

1

Patches

1
566054ba11f6

MDL-87612 repository: safer unserializing of file source data.

https://github.com/moodle/moodlePaul HoldenJan 30, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.