Moodle: insufficient capability checks when updating the parent of a course category
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.
| Package | Affected versions | Patched versions |
|---|---|---|
moodle/moodlePackagist | < 4.3.0-rc2 | 4.3.0-rc2 |
Affected products
3- osv-coords2 versions
< 3.9.24+ 1 more
- (no CPE)range: < 3.9.24
- (no CPE)range: < 4.3.0-rc2
Patches
15a765e124c95MDL-66730 core_course: Improve permission check for category moving
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- github.com/advisories/GHSA-fm5h-58g2-4m3fghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-5549ghsaADVISORY
- bugzilla.redhat.com/show_bug.cgighsaissue-trackingx_refsource_REDHATWEB
- github.com/moodle/moodle/commit/5a765e124c950b1e4313c9bf96ea2dd194f65c75ghsaWEB
- moodle.org/mod/forum/discuss.phpghsaWEB
News mentions
0No linked articles in our index yet.