VYPR
High severity7.1NVD Advisory· Published Jun 8, 2026

CVE-2026-48507

CVE-2026-48507

Description

Snipe-IT versions prior to 8.6.0 allow non-admins with user edit permissions to lock out all admins by disabling logins and password resets.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Snipe-IT versions prior to 8.6.0 allow non-admins with user edit permissions to lock out all admins by disabling logins and password resets.

Vulnerability

A vulnerability exists in Snipe-IT versions prior to 8.6.0 that allows a non-administrator user, who possesses the granular users.edit permission, to lock out all administrative users from the instance. This is achieved by modifying the activated flag, which controls user login capabilities, and the ldap_import flag, which affects the password reset functionality [2].

Exploitation

An attacker with a non-administrative account that has the users.edit permission can exploit this vulnerability. The attacker needs to edit the activated and ldap_import flags for admin users. By setting the activated flag to disable login and the ldap_import flag to prevent password resets, the attacker can effectively lock out all administrators [2].

Impact

Successful exploitation of this vulnerability results in the denial of service for all administrative users. Administrators will be unable to log in to the Snipe-IT instance, and they will also be unable to reset their passwords if the ldap_import flag is also manipulated. This effectively locks out all privileged users from managing the system [2].

Mitigation

Snipe-IT version 8.6.0 contains a patch for this vulnerability [1]. Users are advised to upgrade to version 8.6.0 or later to remediate the issue. No workarounds are described in the available references.

AI Insight generated on Jun 8, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

1
403f9c848b05

Disallow ldap_import and activated in bulk editing users if user doesn’t have permission

https://github.com/grokability/snipe-itsnipeMay 21, 2026via nvd-ref
5 files changed · +255 8
  • app/Http/Controllers/Users/BulkUsersController.php+20 8 modified
    @@ -171,8 +171,6 @@ public function update(Request $request)
                 ->conditionallyAddItem('company_id')
                 ->conditionallyAddItem('locale')
                 ->conditionallyAddItem('remote')
    -            ->conditionallyAddItem('ldap_import')
    -            ->conditionallyAddItem('activated')
                 ->conditionallyAddItem('display_name')
                 ->conditionallyAddItem('start_date')
                 ->conditionallyAddItem('end_date')
    @@ -235,11 +233,21 @@ public function update(Request $request)
                     ->update(['location_id' => $this->update_array['location_id']]);
             }
     
    -        // Only sync groups if groups were selected
    -        if ($request->filled('groups')) {
    -
    -            foreach ($users as $user) {
    -                if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
    +        // Fields that require canEditAuthFields (non-admins cannot touch admins/superusers,
    +        // admins cannot touch superusers) must be applied per-user, not via mass update.
    +        foreach ($users as $user) {
    +            if (auth()->user()->can('canEditAuthFields', $user) && auth()->user()->can('editableOnDemo')) {
    +                $authFieldUpdate = [];
    +                if ($request->filled('activated')) {
    +                    $authFieldUpdate['activated'] = $request->input('activated');
    +                }
    +                if ($request->filled('ldap_import')) {
    +                    $authFieldUpdate['ldap_import'] = $request->input('ldap_import');
    +                }
    +                if (! empty($authFieldUpdate)) {
    +                    $user->update($authFieldUpdate);
    +                }
    +                if ($request->filled('groups')) {
                         $user->groups()->sync($request->input('groups'));
                     }
                 }
    @@ -398,7 +406,7 @@ private function logAccessoriesCheckin(Collection $accessoryUserRows): void
          */
         public function merge(Request $request)
         {
    -        $this->authorize('update', User::class);
    +        $this->authorize('delete', User::class);
     
             if (config('app.lock_passwords')) {
                 return redirect()->route('users.index')->with('error', trans('general.feature_disabled'));
    @@ -419,6 +427,10 @@ public function merge(Request $request)
             // Walk users
             foreach ($users_to_merge as $user_to_merge) {
     
    +            if (! auth()->user()->can('canEditAuthFields', $user_to_merge) || ! auth()->user()->can('editableOnDemo')) {
    +                return redirect()->route('users.index')->with('error', trans('general.insufficient_permissions'));
    +            }
    +
                 foreach ($user_to_merge->assets as $asset) {
                     Log::debug('Updating asset: '.$asset->asset_tag.' to '.$merge_into_user->id);
                     $asset->assigned_to = $request->input('merge_into_id');
    
  • resources/lang/en-US/general.php+1 0 modified
    @@ -679,6 +679,7 @@
         'user_managed_passwords' => 'Password Management',
         'user_managed_passwords_disallow' => 'Disallow users from managing their own passwords',
         'user_managed_passwords_allow' => 'Allow users to manage their own passwords',
    +    'user_managed_passwords_bulk_help' => 'This setting will only be applied to users you are abler to edit authentication settings on.',
         'from' => 'From',
         'by' => 'By',
         'by_user' => 'By',
    
  • resources/views/users/bulk-edit.blade.php+1 0 modified
    @@ -159,6 +159,7 @@
                                             <input type="radio" name="ldap_import" id="ldap_import" value="1" aria-label="ldap_import">
                                             {{ trans('general.user_managed_passwords_disallow') }}
                                         </label>
    +                                    <p class="help-block">{{ trans('general.user_managed_passwords_bulk_help') }}</p>
                                 </div>
                             </div> <!--/form-group-->
     
    
  • tests/Feature/Users/Ui/BulkActions/BulkEditUsersTest.php+124 0 added
    @@ -0,0 +1,124 @@
    +<?php
    +
    +namespace Tests\Feature\Users\Ui\BulkActions;
    +
    +use App\Models\Asset;
    +use App\Models\User;
    +use Tests\TestCase;
    +
    +class BulkEditUsersTest extends TestCase
    +{
    +    public function test_requires_correct_permission()
    +    {
    +        $this->actingAs(User::factory()->create())
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [User::factory()->create()->id],
    +            ])
    +            ->assertForbidden();
    +    }
    +
    +    public function test_non_admin_cannot_deactivate_admin_via_bulk_edit()
    +    {
    +        $actor = User::factory()->editUsers()->create();
    +        $admin = User::factory()->admin()->create(['activated' => 1]);
    +
    +        $this->actingAs($actor)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$admin->id],
    +                'activated' => '0',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals(1, $admin->fresh()->activated);
    +    }
    +
    +    public function test_non_admin_cannot_deactivate_superuser_via_bulk_edit()
    +    {
    +        $actor = User::factory()->editUsers()->create();
    +        $superuser = User::factory()->superuser()->create(['activated' => 1]);
    +
    +        $this->actingAs($actor)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$superuser->id],
    +                'activated' => '0',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals(1, $superuser->fresh()->activated);
    +    }
    +
    +    public function test_admin_cannot_deactivate_superuser_via_bulk_edit()
    +    {
    +        $admin = User::factory()->admin()->create();
    +        $superuser = User::factory()->superuser()->create(['activated' => 1]);
    +
    +        $this->actingAs($admin)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$superuser->id],
    +                'activated' => '0',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals(1, $superuser->fresh()->activated);
    +    }
    +
    +    public function test_non_admin_can_deactivate_regular_user_via_bulk_edit()
    +    {
    +        $actor = User::factory()->editUsers()->create();
    +        $target = User::factory()->create(['activated' => 1]);
    +
    +        $this->actingAs($actor)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$target->id],
    +                'activated' => '0',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals(0, $target->fresh()->activated);
    +    }
    +
    +    public function test_admin_can_deactivate_regular_user_via_bulk_edit()
    +    {
    +        $admin = User::factory()->admin()->create();
    +        $target = User::factory()->create(['activated' => 1]);
    +
    +        $this->actingAs($admin)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$target->id],
    +                'activated' => '0',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals(0, $target->fresh()->activated);
    +    }
    +
    +    public function test_non_admin_cannot_set_ldap_import_on_admin_via_bulk_edit()
    +    {
    +        $actor = User::factory()->editUsers()->create();
    +        $admin = User::factory()->admin()->create(['ldap_import' => 0]);
    +
    +        $this->actingAs($actor)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$admin->id],
    +                'ldap_import' => '1',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals(0, $admin->fresh()->ldap_import);
    +    }
    +
    +    public function test_non_auth_fields_are_still_updated_for_admin_targets()
    +    {
    +        $actor = User::factory()->editUsers()->create();
    +        $admin = User::factory()->admin()->create(['city' => 'Springfield']);
    +
    +        $this->actingAs($actor)
    +            ->post(route('users/bulkeditsave'), [
    +                'ids' => [$admin->id],
    +                'city' => 'Shelbyville',
    +            ])
    +            ->assertRedirect(route('users.index'));
    +
    +        $this->assertEquals('Shelbyville', $admin->fresh()->city);
    +    }
    +}
    
  • tests/Feature/Users/Ui/BulkActions/BulkMergeUsersTest.php+109 0 added
    @@ -0,0 +1,109 @@
    +<?php
    +
    +namespace Tests\Feature\Users\Ui\BulkActions;
    +
    +use App\Models\Asset;
    +use App\Models\User;
    +use Tests\TestCase;
    +
    +class BulkMergeUsersTest extends TestCase
    +{
    +    public function test_requires_delete_permission()
    +    {
    +        $target = User::factory()->create();
    +        $to_merge = User::factory()->create();
    +
    +        $this->actingAs(User::factory()->editUsers()->create())
    +            ->post(route('users.merge.save'), [
    +                'ids_to_merge' => [$to_merge->id],
    +                'merge_into_id' => $target->id,
    +            ])
    +            ->assertForbidden();
    +
    +        $this->assertNotSoftDeleted($to_merge);
    +    }
    +
    +    public function test_non_admin_cannot_merge_admin_into_self()
    +    {
    +        $actor = User::factory()->deleteUsers()->create();
    +        $admin = User::factory()->admin()->create();
    +
    +        $this->actingAs($actor)
    +            ->post(route('users.merge.save'), [
    +                'ids_to_merge' => [$admin->id],
    +                'merge_into_id' => $actor->id,
    +            ])
    +            ->assertRedirect(route('users.index'))
    +            ->assertSessionHas('error');
    +
    +        $this->assertNotSoftDeleted($admin);
    +    }
    +
    +    public function test_non_admin_cannot_merge_superuser_into_self()
    +    {
    +        $actor = User::factory()->deleteUsers()->create();
    +        $superuser = User::factory()->superuser()->create();
    +
    +        $this->actingAs($actor)
    +            ->post(route('users.merge.save'), [
    +                'ids_to_merge' => [$superuser->id],
    +                'merge_into_id' => $actor->id,
    +            ])
    +            ->assertRedirect(route('users.index'))
    +            ->assertSessionHas('error');
    +
    +        $this->assertNotSoftDeleted($superuser);
    +    }
    +
    +    public function test_admin_cannot_merge_superuser_into_self()
    +    {
    +        $admin = User::factory()->admin()->create();
    +        $superuser = User::factory()->superuser()->create();
    +
    +        $this->actingAs($admin)
    +            ->post(route('users.merge.save'), [
    +                'ids_to_merge' => [$superuser->id],
    +                'merge_into_id' => $admin->id,
    +            ])
    +            ->assertRedirect(route('users.index'))
    +            ->assertSessionHas('error');
    +
    +        $this->assertNotSoftDeleted($superuser);
    +    }
    +
    +    public function test_assets_are_transferred_and_source_user_is_deleted_on_merge()
    +    {
    +        $admin = User::factory()->admin()->create();
    +        $source = User::factory()->create();
    +        $target = User::factory()->create();
    +        $asset = Asset::factory()->assignedToUser($source)->create();
    +
    +        $this->actingAs($admin)
    +            ->post(route('users.merge.save'), [
    +                'ids_to_merge' => [$source->id],
    +                'merge_into_id' => $target->id,
    +            ])
    +            ->assertRedirect(route('users.index'))
    +            ->assertSessionHas('success');
    +
    +        $this->assertSoftDeleted($source);
    +        $this->assertEquals($target->id, $asset->fresh()->assigned_to);
    +    }
    +
    +    public function test_merge_does_not_transfer_assets_when_source_is_protected()
    +    {
    +        $actor = User::factory()->deleteUsers()->create();
    +        $admin = User::factory()->admin()->create();
    +        $asset = Asset::factory()->assignedToUser($admin)->create();
    +
    +        $this->actingAs($actor)
    +            ->post(route('users.merge.save'), [
    +                'ids_to_merge' => [$admin->id],
    +                'merge_into_id' => $actor->id,
    +            ])
    +            ->assertRedirect(route('users.index'))
    +            ->assertSessionHas('error');
    +
    +        $this->assertEquals($admin->id, $asset->fresh()->assigned_to);
    +    }
    +}
    

Vulnerability mechanics

Root cause

"A non-admin user with the granular `users.edit` permission could modify critical user flags like `activated` and `ldap_import` for other users, including administrators."

Attack vector

A non-administrator user with the `users.edit` permission can exploit this vulnerability. By targeting an administrator account and modifying the `activated` flag to `0`, the attacker can effectively lock the administrator out of the system. Additionally, manipulating the `ldap_import` flag can prevent users from resetting their passwords, further exacerbating the lockout scenario. This can be achieved through bulk editing operations [patch_id=5239709].

Affected code

The vulnerability lies within the `BulkUsersController` in the `update` method, specifically where fields like `ldap_import` and `activated` were previously handled in a mass update operation. The patch modifies this section to enforce granular permissions on these fields [patch_id=5239709].

What the fix does

The patch modifies the `BulkUsersController` to restrict the bulk editing of sensitive fields like `activated` and `ldap_import` [patch_id=5239709]. These fields can now only be updated on a per-user basis if the acting user has the `canEditAuthFields` permission and the target user is editable on demo. This prevents lower-privileged users from deactivating administrators or altering critical authentication flags for privileged accounts.

Preconditions

  • authThe attacker must be a non-administrator user.
  • authThe attacker must possess the granular `users.edit` permission.

Generated on Jun 8, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.