CVE-2026-86760 Overview
CVE-2026-86760 is an incorrect authorization vulnerability [CWE-863] in Snipe-IT, an open-source IT asset management application. The flaw affects Snipe-IT versions 8.2.0 through 8.6.x and is fixed in 8.7.0. The issue lives in app/Http/Controllers/Users/UsersController::update(), where the activated field is assigned from the request payload before the canEditAuthFields authorization gate runs. An authenticated non-admin user holding the users.edit permission in the target's company scope can submit a full valid PUT /users/{id} request and toggle the activated flag on any account, including admins and superusers.
Critical Impact
A low-privileged user with users.edit scope can deactivate admin or superuser accounts, locking them out of Snipe-IT until another admin re-enables them.
Affected Products
- Snipe-IT 8.2.0 through 8.6.x
- Snipe-IT web UsersController::update() single-user edit route
- Fixed in Snipe-IT 8.7.0
Discovery Timeline
- 2026-09-09 - CVE-2026-86760 published to NVD
- 2026-09-09 - Last updated in NVD database
Technical Details for CVE-2026-86760
Vulnerability Analysis
The vulnerability is a broken access control issue in the single-user edit path of Snipe-IT. The controller assigned the activated attribute from user-controlled input before checking whether the caller had permission to modify authentication-related fields. The canEditAuthFields gate only executes afterward, and it guards a separate block that overwrites username, email, password, and permissions. Because activated was set outside that block, the gate never covered it. Any authenticated user holding users.edit in the target's company scope can therefore flip the account state of an admin or superuser. The impact is limited to account availability. Credentials and permissions remain protected, no data is disclosed, and neither the JSON API (Api\UsersController::update) nor the bulk-edit workflow is affected.
Root Cause
The root cause is a field assignment ordered ahead of its authorization check. The original code executed $user->activated = $request->input('activated', ...) before the if (auth()->user()->can('canEditAuthFields', $user) ...) branch. Developers assumed the later gate would overwrite the earlier assignment, but the gate only re-assigns activated when the caller passes the check. When the caller fails the check, the first, unauthorized assignment persists.
Attack Vector
An attacker authenticates to Snipe-IT with a low-privilege account that holds users.edit in the same company scope as the target. The attacker submits a full valid PUT /users/{id} payload containing activated=0 against an admin's user ID. The controller writes the deactivation before the authorization gate blocks the other fields, and the admin account is locked out.
$user->end_date = $request->input('end_date', null);
$user->autoassign_licenses = $request->input('autoassign_licenses', 0);
- // Set this here so that we can overwrite it later if the user is an admin or superadmin
- $user->activated = $request->input('activated', auth()->user()->is($user) ? 1 : $user->activated);
-
// Update the location of any assets checked out to this user
Asset::where('assigned_type', User::class)
->where('assigned_to', $user->id)
->update(['location_id' => $request->input('location_id', null)]);
- // check for permissions related fields and only set them if the user has permission to edit them
+ // Permission-gated fields: `activated` lives inside this gate too.
+ // An earlier version of this method assigned `activated` right
+ // before the gate on the theory that the gate would overwrite it.
+ // That let anyone with users.edit toggle an admin's activated flag
+ // by POSTing a full edit payload — the gate would deny the second
+ // assignment but the first had already stuck. Every auth-field
+ // write must live inside this branch so an unauthorized caller
+ // can't reach past the gate on any of them.
if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
$user->username = trim($request->input('username'));
Source: GitHub Commit 02f62c8
Detection Methods for CVE-2026-86760
Indicators of Compromise
- Admin or superuser accounts unexpectedly showing activated=0 in the users table.
- PUT /users/{id} requests from non-admin sessions targeting admin user IDs.
- Sudden login failures or lockouts reported by administrators immediately after a user-edit action by a low-privileged operator.
Detection Strategies
- Audit Laravel application logs for UsersController@update invocations where the actor lacks the canEditAuthFields capability but the target is an admin or superuser.
- Correlate database updates on the users.activated column against the acting user's role and permission scope.
- Enable and review Snipe-IT's activity log for User model updates that change the activated state on privileged accounts.
Monitoring Recommendations
- Alert on any change to the activated flag on accounts flagged as admin or superuser.
- Monitor web server access logs for PUT /users/{id} calls originating from sessions belonging to non-privileged users.
- Track help-desk tickets for admin lockout patterns that coincide with recent user-edit activity.
How to Mitigate CVE-2026-86760
Immediate Actions Required
- Upgrade Snipe-IT to version 8.7.0 or later, which moves the activated assignment inside the canEditAuthFields gate.
- Review the users.edit permission assignments and remove it from accounts that do not require user-management capability.
- Audit the activated state of all admin and superuser accounts and re-enable any that were deactivated without authorization.
Patch Information
The fix is committed in 02f62c8dce7bee118a07882823cd1ad098360a17 and shipped in Snipe-IT 8.7.0. The patch removes the pre-gate assignment and relocates the activated write inside the if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) block. See the GitHub Security Advisory GHSA-xq9q-777w-q9h7 and the VulnCheck Advisory for Snipe-IT for advisory details.
Workarounds
- Restrict the users.edit permission to trusted administrators until the upgrade is complete.
- Segment company scopes so that low-privileged operators cannot share a scope with admin or superuser accounts.
- Enable multi-admin coverage so a deactivated admin can be re-enabled quickly by another privileged user.
# Upgrade Snipe-IT to the patched release
cd /var/www/snipe-it
git fetch --tags
git checkout v8.7.0
composer install --no-dev --prefer-source
php artisan migrate --force
php artisan config:clear
php artisan cache:clear
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

