VYPR
Moderate severityOSV Advisory· Published Nov 9, 2023· Updated Aug 2, 2024

Moodle: insufficient capability checks when updating the parent of a course category

CVE-2023-5549

Description

Moodle improperly validates category management capability when moving categories, allowing users to move manageable categories to protected parent categories.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Moodle improperly validates category management capability when moving categories, allowing users to move manageable categories to protected parent categories.

Overview

The vulnerability CVE-2023-5549 affects Moodle, an open-source learning platform. It is rooted in core_course_external::update_categories(), which updates course categories via web service calls. Before the fix, the code only checked that the user had the moodle/category:manage capability in the context of the *source* category being moved. It did not verify that the user also had that same capability in the context of the *destination* parent category. This insufficient server-side capability check means a user could move a category they controlled into a parent category for which they lacked management permission [1][2][3].

Exploitation

An authenticated user with the moodle/category:manage capability on a specific child category could exploit this flaw. By crafting a web service request that changes the parent field of the category to an ID of a top-level category or another category they do not manage, the user could relocate the category. No authentication bypass or network-level attack is required; the attacker must already have a role that assigns the category management capability on the source category [1][2]. The fix, introduced in commit 5a765e1, adds a check in update_categories() that validates moodle/category:manage in the context of the new parent category (or the system context if the parent is 0) before allowing the move [3].

Impact

Successful exploitation allows a user to reorganize course categories in ways that violate the intended access control policies. An attacker could, for example, nest a category containing sensitive courses under a top-level category that other users manage, or disrupt the logical structure of the site. This could lead to unintended exposure of category contents or administrative confusion, though the vulnerability does not grant code execution or direct data access [1][2].

Mitigation

The vulnerability has been patched in Moodle versions 4.3, 4.2, and 4.1 following the disclosure and is tracked as MSA-23-0041. Sites running affected versions should update to the latest maintenance releases immediately. No workarounds are documented; the fix requires applying the updated code [2][3].

AI Insight generated on May 20, 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
< 4.3.0-rc24.3.0-rc2

Affected products

3

Patches

1
5a765e124c95

MDL-66730 core_course: Improve permission check for category moving

https://github.com/moodle/moodleHuong NguyenSep 18, 2023via ghsa
2 files changed · +51 0
  • course/externallib.php+13 0 modified
    @@ -2202,6 +2202,19 @@ public static function update_categories($categories) {
                 self::validate_context($categorycontext);
                 require_capability('moodle/category:manage', $categorycontext);
     
    +            // If the category parent is being changed, check for capability in the new parent category
    +            if (isset($cat['parent']) && ($cat['parent'] !== $category->parent)) {
    +                if ($cat['parent'] == 0) {
    +                    // Creating a top level category requires capability in the system context
    +                    $parentcontext = context_system::instance();
    +                } else {
    +                    // Category context
    +                    $parentcontext = context_coursecat::instance($cat['parent']);
    +                }
    +                self::validate_context($parentcontext);
    +                require_capability('moodle/category:manage', $parentcontext);
    +            }
    +
                 // this will throw an exception if descriptionformat is not valid
                 util::validate_format($cat['descriptionformat']);
     
    
  • course/tests/externallib_test.php+38 0 modified
    @@ -365,6 +365,44 @@ public function test_update_categories() {
             core_course_external::update_categories($categories);
         }
     
    +    /**
    +     * Test update_categories method for moving categories
    +     */
    +    public function test_update_categories_moving() {
    +        $this->resetAfterTest();
    +
    +        // Create data.
    +        $categorya  = self::getDataGenerator()->create_category([
    +            'name' => 'CAT_A',
    +        ]);
    +        $categoryasub = self::getDataGenerator()->create_category([
    +            'name' => 'SUBCAT_A',
    +            'parent' => $categorya->id
    +        ]);
    +        $categoryb  = self::getDataGenerator()->create_category([
    +            'name' => 'CAT_B',
    +        ]);
    +
    +        // Create a new test user.
    +        $testuser = self::getDataGenerator()->create_user();
    +        $this->setUser($testuser);
    +
    +        // Set the capability for CAT_A only.
    +        $contextcata = context_coursecat::instance($categorya->id);
    +        $roleid = $this->assignUserCapability('moodle/category:manage', $contextcata->id);
    +
    +        // Then we move SUBCAT_A parent: CAT_A => CAT_B.
    +        $categories = [
    +            [
    +                'id' => $categoryasub->id,
    +                'parent' => $categoryb->id
    +            ]
    +        ];
    +
    +        $this->expectException('required_capability_exception');
    +        core_course_external::update_categories($categories);
    +    }
    +
         /**
          * Test create_courses numsections
          */
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.