CVE-2026-50280 — cms
Fix: craftcms/cms@0a6b916CVE-2026-50280 is a CWE-284 vulnerability in craftcms/cms. A fix is available for craftcms/cms — see the affected versions and patch details below.
Craft CMS: Authorization bypass in `entries/move-to-section` via missing target-section save check
Exploitation Status
No confirmed exploitation observed yet
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-50280.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
Real-World Exposure
craftcms/cmsReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The 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.
Details
The vulnerable route is implemented in EntriesController.php:465:
The 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:
This closes the exploit chain:
- External source: authenticated CP request to
entries/move-to-section. - Missing authorization check: destination section requires only
viewEntries, notsaveEntries. - Privileged sink:
moveEntryToSection()rewritessectionIdand saves the entry into the unauthorized section.
Preconditions derived from the code:
- The attacker is authenticated to the control panel.
- Entry
345is movable by the attacker from its current section. - The attacker can satisfy
viewEntrieson destination section12. - The attacker does not have
saveEntries:DESTINATION_UID, which is the missing check that makes the bypass possible.
Result:
- The controller accepts the request because
viewEntries:$section->uidpasses. - Each source entry passes
canMove()based on source-section permissions. moveEntryToSection()updates the entry’ssectionIdand saves it.- The entry is now located in a section where the attacker did not have write permission.
Impact
This 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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | craftcms/cms | ≥ 5.0.0-RC1&&< 5.9.21 | 5.9.21composer require craftcms/cms:^5.9.21 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for craftcms/cms, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update craftcms/cms to 5.9.21 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-50280 is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Frequently Asked Questions
Is CVE-2026-50280 in your dependencies?
Find it across Packagist, including transitive dependencies.