Snipe-IT resolved a SAML-authenticated user by taking the SAML username attribute (or NameID when no mapping is configured) and running an Eloquent equality query against the users.username column. On MySQL/MariaDB the default collation for that column is utf8mb4_unicode_ci , which is both case-insensitive AND accent-insensitive. A signed SAML assertion for snípeitreport3 therefore resolved to the existing local Snipe-IT user snipeitreport3 , even though the two strings are different byte sequences and represent different IdP accounts. The SAML login path then authenticated the browser session as the wrong local Snipe-IT user, achieving account takeover.
The same class of bypass applied to any federated login path that hit the same WHERE username = ? shape (LDAP, REMOTE_USER , Google OAuth).
Already fixed in commit 2304066d79 on 2026-07-11 ("Auth: Fixed FD-56498 - check username case for non-local logins"). This advisory documents the vulnerability for CVE assignment and public disclosure once the fix has shipped in a tagged release.
High . CVSS 3.1 base score 8.1 with vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N .
Primary: CWE-178 (Improper Handling of Case Sensitivity) . The core defect: an authentication lookup treats byte-distinct strings as equivalent because the underlying database collation folds case and accents.
Related: CWE-287 (Improper Authentication) . Ultimate consequence: the wrong local account is authenticated.
Also relevant: CWE-706 (Use of Incorrectly-Resolved Name or Reference) . The name-to-account resolution step is the specific weakness.
Confirmed at Snipe-IT v8.6.3 (Docker image digest snipe/snipe-it@sha256:6476be7cca765880374a6d489d22ad43d3edba965ab3cb862bf828f5fc856e50 ). Likely affects all versions where the SAML login path ran a plain WHERE username = ? query and the database was configured with the default utf8mb4_unicode_ci collation (which has been the Snipe-IT default for many releases). Fixed on the develop branch in commit 2304066d79 . The first tagged release containing the fix will be filled in at publication.
Root cause: database collation on users.username
The migration that creates the username column does not force a byte-preserving collation:
( database/migrations/2014_11_20_203007_add_username_to_user.php:17 )
Snipe-IT's config/database.php defaults to utf8mb4_unicode_ci :
Under that collation, the database treats byte-distinct strings as equal:
app/Services/Saml.php::samlLogin() prior to the fix:
$username came from the attacker-controlled SAML username attribute (or NameID ), was substituted directly into the query, and MySQL / MariaDB's collation resolved the accented spelling to the plain-ASCII victim row. No byte-exact post-filter guarded the result before it was returned to the caller.
The returned row was trusted
LoginController::samlLogin() called Auth::login($user) on whatever samlLogin() returned, completing the session takeover.
Where the SAML username came from
Saml::getUsername() selects the NameID by default, then replaces it with the attribute named by saml_attr_mapping_username if that setting is configured:
Either path is attacker-controlled at the IdP.
Same class of bug applied to other federated login paths
LoginController and GoogleAuthController had structurally similar WHERE username = ? lookups that inherited the same collation folding. The fix commit closes all of them, not just the SAML path.
Reproduced by the researcher (whale120) against a fresh Snipe-IT v8.6.3 Docker deployment with a Keycloak IdP.
Snipe-IT snipe/snipe-it:v8.6.3
MariaDB 11.8, users.username collation utf8mb4_unicode_ci
Keycloak realm saml-collation-poc with self-registration enabled
Victim (existing local Snipe-IT account and matching IdP account):
Snipe-IT username snipeitreport3 (database id 4)
Full name "Snipe VictimThree"
Attacker registers a new Keycloak account with username snípeitreport3 (Latin small letter i with acute accent).
Attacker initiates SP-initiated SAML SSO at .
Keycloak issues a signed SAML response with: NameID : snípeitreport3 Attribute username : snípeitreport3
NameID : snípeitreport3
Attribute username : snípeitreport3
Snipe-IT ACS accepts the assertion, runs the vulnerable query, and returns the victim's local user row (id 4, snipeitreport3 ) because utf8mb4_unicode_ci folds the accent.
Auth::login($victimUser) completes the session takeover.
Attacker is redirected to /account/profile and sees "Snipe VictimThree".
Database state after the attack:
No local Snipe-IT account with that byte sequence exists. The attacker's session is nonetheless bound to snipeitreport3 (id 4).
Applied in commit 2304066d79 . Introduces a shared post-filter in App\Models\User :
Each federated login path ( Saml.php , LoginController , GoogleAuthController ) now runs its username lookup and then calls verifyExactUsernameMatch() before completing authentication. hash_equals() compares byte-for-byte in constant time, so the check does not leak timing information.
Post-fix Saml.php::samlLogin() :
Snipe-IT chose the post-filter approach over an ALTER TABLE ... COLLATE utf8mb4_bin migration because:
Changing the column collation would migrate every existing installation, requiring maintenance downtime for large deployments, and would silently break case-insensitive username display or patterns that admins may rely on today.
Keeping display and case-insensitive while making authentication byte-exact matches the reporter's alternative recommendation and preserves operator expectations.
The post-filter closes the specific attack vector (federated authentication paths that trust the collation-folded lookup) without changing every other query that touches the username column.
2026-07-02: Vulnerability discovered by whale120 during the DEVCORE Internship Program.
2026-07-11: Fix committed as 2304066d79 on the develop branch (internal ticket FD-56498).
Vulnerability reported by whale120 ( @whale120_tw ) working with the DEVCORE Internship Program. Internally tracked as FD-56498.
CWE-178: Improper Handling of Case Sensitivity
CWE-287: Improper Authentication
CWE-706: Use of Incorrectly-Resolved Name or Reference
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
