Craft CMS sites running Pro edition versions 5.6.0 through 5.9.14 expose a critical authorization flaw. Any control panel user with just viewUsers permission can strip arbitrary target users from all their groups. This revokes group-granted permissions instantly, without needing elevated session checks or edit privileges.
The bug stems from a regression in 5.6.0, when developers added viewUsers as a read-only permission. Before that, accessing other users’ data required editUsers, blocking the vulnerable endpoint. Now, actionSavePermissions() opens to low-priv users, and the inner _saveUserGroups() function skips authorization entirely on removals.
How the Exploit Works
Attackers need only accessCp and viewUsers. They POST to actions/users/save-permissions with the target’s userId and an empty groups string. The code treats this as an empty array, skips its addition-only permission loop, and wipes all memberships.
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "userId=123&groups=" \
https://yoursite.com/actions/users/save-permissions
No JavaScript or CSRF tricks required—just a direct Control Panel request. Group removal bypasses requireElevatedSession(), which only triggers on additions. Craft CMS, built on Yii2, gates this behind Pro edition features like advanced user groups.
This isn’t theoretical. Craft powers thousands of production sites, often for agencies handling client data. User groups commonly control access to sections, asset volumes, and plugins. A rogue freelancer or compromised junior account could lock out admins overnight.
Real-World Impact and Why It Matters
Primary effect: privilege revocation. Targets lose all group-derived permissions immediately. Admins drop to base account rights; editors lose section access. In multi-tenant setups—common in Craft Pro—this cascades to denied logins across teams.
Consider a marketing agency site: client portals use groups for restricted volumes. An attacker demotes the client admin, blocking uploads and previews. Or in e-commerce: strip inventory managers from their groups, halting orders during peak hours.
Craft markets itself as secure, with a strong track record versus WordPress bloat. But regressions like this erode trust. Similar issues hit other CMSes—Drupal’s 2018 SA-CORE-2018-002 allowed role demotion; WordPress plugins repeat empty-array bypasses yearly. Craft’s Pro-only gating limits blast radius, but 5.6.0+ installs number in the thousands per Craft’s stats (over 100k total sites, ~20% Pro).
Broader implications: enforce least privilege rigorously. viewUsers sounds harmless, but it now grants mass-demotion power. Audit your groups—who inherits what? Craft’s permission system relies on symmetry; this breaks it for removals only.
Financial angle: downtime from locked admins costs hours in dev time. For high-value sites, pair with Craft’s two-factor auth and IP whitelisting. Crypto/security pros: treat CMS Control Panels like root shells. Segment users into minimal groups, log all permission changes via plugins like Audit Log.
Mitigation demands urgency. Update past 5.9.14—Craft released 5.9.15+ with fixes (confirm via changelog). Revoke unnecessary viewUsers grants. Test: create a view-only account, attempt the POST. If it works, you’re exposed.
Post-patch, monitor logs for save-permissions hits with empty groups. Craft’s security team patched regressions before; this flags deeper review needs for permission asymmetry. Users: demand changelogs specify vulns. Developers: always test removals symmetrically.
Bottom line: low-priv escalation via demotion beats classic priv-esc. It flips access controls inverted. If your Craft Pro site handles sensitive data, patch now, audit perms, and rethink read-only assumptions. Craft remains solid, but no CMS is regression-proof.