Shopper: privilege escalation via improper Livewire admin component authorization
Description
Summary
Three Livewire admin components in shopper/framework (latest master at commit fcd0c59, released as v2.8.0) gate state-mutating actions on the read-only view_users permission. This is the same class as the issue Shopper fixed in v2.8.0 / PR #511 / GHSA-f946-9qp6-vgch — the PR moved most write actions from view_users to access_setting, but three were missed (one of them is a brand-new file added by the security commit itself).
A staff user holding only view_users + access_dashboard (a realistic "support" or "viewer" role per Shopper's own PermissionsTableSeeder) can: (1) self-escalate by granting any permission to their own role; (2) create a brand-new admin team member with a chosen password and the admin role and then log in as that user; (3) delete arbitrary permissions rows (RBAC DoS) or — when can_be_removed=true — delete entire roles.
CVSS 3.1: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8 (High). CWE-285 (Improper Authorization) + CWE-862 (Missing Authorization).
Vulnerable components (paths relative to repo root)
1) packages/admin/src/Livewire/Components/Settings/Team/Permissions.php
togglePermission(int $id)at line 28 calls$this->authorize('view_users');removePermission(int $id)at line 55 calls$this->authorize('view_users');
The Permissions blade at packages/admin/resources/views/livewire/components/settings/team/permissions.blade.php line 34 emits every permission's id directly in wire:click handlers, so the attacker does not even need to guess IDs — the page itself enumerates them.
Net effect: any user who can mount the Permissions component (gated on view_users) can grant any permission row to the bound $role. Granting access_setting to the attacker's own role unlocks every action that PR #511 supposedly hardened with ->authorize('access_setting'). Granting delete_customers, edit_orders, edit_products, add_brands, etc. is direct data-modification escalation.
2) packages/admin/src/Livewire/SlideOvers/CreateTeamMember.php
mount()at line 53 calls$this->authorize('view_users');store()at line 122 calls$this->authorize('view_users');
This file is new file mode 100755 in commit fcd0c59 — it was created as part of the security fix and inherited the same misclassified gate.
store() creates a User with email_verified_at = now(), the attacker's chosen password, and any selected role_id. The Radio::make('role_id') options filter only excludes config('shopper.admin.roles.user'), so the admin role is selectable. Log out, log in as the new account → full admin.
3) packages/admin/src/Livewire/Pages/Settings/Team/RolePermission.php
deleteActionat lines 81-90: only gated by->visible($this->role->can_be_removed), with no->authorize()chain.
Page-level mount (line 52) requires only view_users. For any role with can_be_removed = true, a view_users-only user can call the action and delete the role (cascading the loss of permissions for every assigned user).
Self-confirmation in the project's own test suite
The following tests are green on master @ fcd0c59 — they ARE the PoC:
tests/Admin/Livewire/Components/Settings/Team/PermissionsTest.php
line 14-16: `givePermissionTo('view_users')` only
line 36-45: "can toggle permission to role" — passes
line 74-85: "can remove permission" — passes
tests/Admin/Livewire/SlideOvers/CreateTeamMemberTest.php
line 16-18: `givePermissionTo('view_users')` only
line 29-56: "can create new team member" — passes, asserts the new user `hasRole('manager')`
A view_users-only Livewire user actor successfully toggles permissions, removes permissions, and creates a new privileged user — verified by Shopper's own regression tests.
Suggested fix
Change $this->authorize('view_users') to $this->authorize('access_setting') in:
Permissions::togglePermissionPermissions::removePermissionPermissions::mount(defence in depth, matchesTeam\Index)CreateTeamMember::mountCreateTeamMember::store
Add ->authorize('access_setting') to RolePermission::deleteAction (matches the pattern already applied to generatePermissionsAction, createPermissionAction, and Team\Index::DeleteAction).
Update the two regression tests to use access_setting instead of view_users so they accurately reflect the privilege boundary.
Resources
- Prior advisory of the same class: https://github.com/shopperlabs/shopper/security/advisories/GHSA-f946-9qp6-vgch
- Fix commit that introduced these residual gaps: https://github.com/shopperlabs/shopper/commit/fcd0c5920588702df5b874f432b1042abd77a50b
- CWE-285 Improper Authorization
- CWE-862 Missing Authorization
Credits
Reported by Vishal Shukla(@shukla304) using sechub.dev AI Agent
Support
If this disclosure was useful and if users would like to support continued open-source security research and responsible-disclosure work, they can sponsor at https://github.com/sponsors/therawdev — Shoppers thanks those who keeping open source safe.
Affected products
1- Range: >= 2.8.0, < 2.9.2
Patches
Vulnerability mechanics
References
2News mentions
0No linked articles in our index yet.