Kestrel
대시보드로 돌아가기
CVE-2026-56828HIGH· 8.8GHSA대응게시일: 2026. 09. 11.수정일: 2026. 09. 11.

Shopper: privilege escalation via improper Livewire admin component authorization

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
8.8high

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한14일 이내CISA SSVC 기준

2주 이내 패치 — 우선 조치 대상

완전 장악외부 노출· KEV 미등재 · 자동화 어려움 · 완전 장악 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도낮음
필요 권한낮음
사용자 상호작용불필요
범위불변
영향
기밀성 영향높음
무결성 영향높음
가용성 영향높음
버전별 점수
CVSS 3.18.8HIGH
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

상세 설명

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

  • deleteAction at 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:

text
1tests/Admin/Livewire/Components/Settings/Team/PermissionsTest.php
2 line 14-16: `givePermissionTo('view_users')` only
3 line 36-45: "can toggle permission to role" — passes
4 line 74-85: "can remove permission" — passes
5
6tests/Admin/Livewire/SlideOvers/CreateTeamMemberTest.php
7 line 16-18: `givePermissionTo('view_users')` only
8 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::togglePermission
  • Permissions::removePermission
  • Permissions::mount (defence in depth, matches Team\Index)
  • CreateTeamMember::mount
  • CreateTeamMember::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

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.

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.