{"id":"CVE-2026-50280","title":"Craft CMS: Authorization bypass in `entries/move-to-section` via missing target-section save check","summary":"Craft CMS: Authorization bypass in `entries/move-to-section` via missing target-section save check","severity":"medium","cwe":["CWE-284"],"vendor":"craftcms","product":"craftcms/cms","ecosystem":"composer","affected":["craftcms/cms >= 5.0.0-RC1, < 5.9.21"],"patched":["craftcms/cms 5.9.21"],"published":"2026-07-02","updated":"2026-07-02","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-43cq-c2gq-pfpw","references":[{"url":"https://github.com/craftcms/cms/security/advisories/GHSA-43cq-c2gq-pfpw"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-50280"},{"url":"https://github.com/craftcms/cms/commit/0a6b916f6367b0162b2eaf2366add67b45fa98ea"},{"url":"https://github.com/advisories/GHSA-43cq-c2gq-pfpw"}],"tags":["ghsa","composer"],"epss":0.00404,"epssPercentile":0.34508,"ingestedAt":"2026-07-02T19:41:50.969Z","slug":"CVE-2026-50280","body":"## Overview\n\n### Summary\n\nThe `EntriesController::actionMoveToSection()` endpoint checks only whether the current user can view the destination section, but it does not require permission to save entries into that section. A low-privileged authenticated control-panel user who can move an entry out of its current section can therefore move that entry into a different section where they have read access but no write access.\n\n### Details\n\nThe vulnerable route is implemented in [EntriesController.php](/D:/files/projects/cms-5.9.19/cms-5.9.19/src/controllers/EntriesController.php):465:\n\nThe destination check is only `viewEntries:$section->uid` . The source-entry gate is `Entry::canMove()`, which verifies whether the user can move the existing entry based on the source section:\n\nThis closes the exploit chain:\n\n1. External source: authenticated CP request to `entries/move-to-section`.\n2. Missing authorization check: destination section requires only `viewEntries`, not `saveEntries`.\n3. Privileged sink: `moveEntryToSection()` rewrites `sectionId` and saves the entry into the unauthorized section.\n\nPreconditions derived from the code:\n\n1. The attacker is authenticated to the control panel.\n2. Entry `345` is movable by the attacker from its current section.\n3. The attacker can satisfy `viewEntries` on destination section `12`.\n4. The attacker does not have `saveEntries:DESTINATION_UID`, which is the missing check that makes the bypass possible.\n\nResult:\n\n1. The controller accepts the request because `viewEntries:$section->uid` passes.\n2. Each source entry passes `canMove()` based on source-section permissions.\n3. `moveEntryToSection()` updates the entry’s `sectionId` and saves it.\n4. The entry is now located in a section where the attacker did not have write permission.\n\n### Impact\n\nThis breaks the intended section-level authorization model. A user with limited content permissions can inject or relocate content into a protected section, interfering with editorial boundaries, approval workflows, section-specific business logic, and content ownership expectations.\n\n## Affected packages\n\n- `craftcms/cms >= 5.0.0-RC1, < 5.9.21`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `craftcms/cms 5.9.21`","depth":"sunlit","depthScore":28,"depthScoreParts":{"impact":27.5,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}