CVE-2026-87016: Open WebUI: Sign-in as another user via wildcard characters in the OAuth subject claim on SQLite
On SQLite, Open WebUI maps an OAuth or SCIM identity to a local account with a substring match, so SQL wildcard characters in the subject value (`%`, `_`) match other accounts. A sign-in can then bind to the wrong account, possibly an administrator. Affected: 0.6.41 up to 0.11.1. Fixed in 0.11.1.
Published Sep 10, 2026 · Analysis Oct 23, 2026
What an attacker can do
get_user_by_oauth_sub and get_user_by_scim_external_id in backend/open_webui/models/users.py call contains() on a generic SQLAlchemy JSON column. On SQLite that compiles to a LIKE with % on both sides of the operand, so % and _ act as wildcards. PostgreSQL takes a separate, correct path and is not affected.
The advisory's test seeded an administrator with subject admin_sub_9999 and a user with bob_sub_1234. Looking up % or admin_sub_% returns the administrator. The database returns the first match, and the sign-in binds to it.
Preconditions limit the attack. The database must be SQLite (the default), and OAuth/OIDC sign-in or SCIM must be enabled (both off by default). To aim at an admin, the attacker must control the claim used as the subject. That happens only if an operator pointed OAUTH_SUB_CLAIM at a user-settable claim such as a username or email, or the provider lets users pick their subject. Standard sub values and the GitHub and Feishu defaults are provider-assigned. Without an attacker, a legitimate subject with an underscore can match several accounts and hand one user another's session, depending on row order. The SCIM path needs the privileged SCIM bearer token. Password sign-in is unaffected.
Who is affected
Affected: open-webui from 0.6.41 up to but not including 0.11.1, on SQLite with OAuth/OIDC or SCIM enabled. Check the version, OAUTH_SUB_CLAIM, and the database backend (DATABASE_URL):
pip show open-webuiPostgreSQL deployments and installs with OAuth, OIDC and SCIM all off are not affected.
open-webui
- Ecosystem
- PyPI
- Affected
- >= 0.6.41, < 0.11.1
- Fixed in
- 0.11.1
- Check
pip show open-webui
How to fix it
Upgrade to Open WebUI 0.11.1 (pull request 28624). No configuration change is needed, and stored identities stay valid.
pip install --upgrade "open-webui>=0.11.1"The lookups now compare the nested JSON value through SQLAlchemy's JSON subscript operator, which gives an exact match on both databases. Until you upgrade, check that OAUTH_SUB_CLAIM points to a provider-assigned claim that users cannot set, and consider moving to PostgreSQL.
How to tell if you were hit
Audit sign-in logs for users who landed in an account that is not theirs, especially administrator sessions from unexpected identities. At your identity provider, look for users whose subject, username or email contains % or _. Compare each Open WebUI OAuth login with the account it resolved to. Review recent admin actions and new API keys.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
On SQLite deployments, the lookup that maps an external identity to a local account does a substring match instead of an exact match. A subject value containing SQL wildcard characters therefore matches accounts the value was never issued for, and the sign-in binds to whichever account the database returns first, which can be an administrator. The same defect affects SCIM external-ID resolution. PostgreSQL deployments are not affected, because they take a separate and correct code path.
Preconditions
- The database is SQLite. This is the default backend. PostgreSQL deployments are not affected at all.
- OAuth or OIDC sign-in is configured, or SCIM provisioning is enabled. Both are off by default.
- For an attacker to steer the match deliberately, they must control the value of the claim Open WebUI uses as the subject. That value is normally assigned by the identity provider and is not attacker-controlled: the shipped GitHub and Feishu configurations use provider-assigned numeric identifiers, and a standard OIDC
subis provider-assigned. The deliberate case therefore requires an operator to have pointedOAUTH_SUB_CLAIMat a claim the end user can set at the identity provider, such as a username or email claim, or an identity provider that lets a user choose their own subject value. - No attacker and no misconfiguration are needed for the accidental case. A legitimate subject value that happens to contain an underscore matches other accounts as well, and which account is returned depends on database row order.
- For the SCIM path, the caller must already hold the SCIM bearer token, which is a privileged credential.
Impact
A sign-in can be bound to an account other than the one the identity provider authenticated. Where the operator has made the subject claim user-settable, an attacker who registers at that provider can choose a value that matches an existing account and receive a session for it, including an administrator account, which is a full compromise of the instance. Where the subject claim is provider-assigned, the deliberate attack is not available, and what remains is a correctness failure in which an ordinary subject value containing an underscore can resolve to the wrong account and hand one user another user's session non-deterministically.
The defect is in the identity match itself, so it is not mitigated by any downstream permission check: by the time a session is issued the wrong account has already been selected. It does not allow account creation, and it does not affect password sign-in, PostgreSQL deployments, or any deployment with OAuth, OIDC and SCIM all disabled.
Fix
Fixed in 0.11.1 by https://github.com/open-webui/open-webui/pull/28624. The two identity lookups now compare the nested JSON value directly through SQLAlchemy's JSON subscript operator, which emits an exact match on both supported databases, instead of going through the column-level contains() operator that degraded to a substring comparison on SQLite. The hand-written per-dialect branching is removed, since the operator already handles both backends.
Upgrading fully resolves it and no configuration change is required. Existing stored identities are unaffected, as the stored format does not change.
Root cause
backend/open_webui/models/users.py—get_user_by_oauth_sub, resolves an OAuth or OIDC identity to a local account.backend/open_webui/models/users.py—get_user_by_scim_external_id, the same pattern for SCIM.- Reached from the OAuth callback handler and the OAuth token-exchange handler, and from the SCIM user routes.
- Affects builds running on SQLite, which is the default database.
The oauth and scim columns are declared with SQLAlchemy's generic JSON type. That type does not implement a containment comparator, so a contains() call against it falls back to the generic string operator and compiles to a LIKE with the operand wrapped in % on both sides. The intent was a JSON containment test; what was emitted was a substring test against the serialized JSON, in which % and _ carry their usual LIKE meaning. The PostgreSQL branch was written separately against JSONB with an equality comparison and is correct, which is why the defect is confined to the default backend and why it survived review: the two branches look symmetrical and only one of them does what it appears to do.
Proof of concept
Against a SQLite instance with an OIDC provider configured, two accounts exist: an administrator whose stored subject is admin_sub_9999, and an ordinary user whose stored subject is bob_sub_1234. Both rows were seeded directly rather than created through a live provider sign-in; the lookup under test was then called as the application calls it.
Resolving the subject value % returns the administrator account. Resolving admin_sub_% likewise returns the administrator account. Resolving the correct full values returns the correct accounts, and resolving an unknown value returns nothing, so the failure is visible only when the supplied value contains a wildcard character. A subsequent check with an ordinary subject value containing an underscore showed it matching more than one account, with the returned account determined by row order.
Credits
Reported by @Classic298.
Credited to Classic298.
Questions
- Can anyone become an admin by using a % character?
Only if they control the subject claim Open WebUI uses. Standard provider-assigned
subvalues do not allow it. It needs an operator to have setOAUTH_SUB_CLAIMto a user-settable claim.- Is PostgreSQL affected by CVE-2026-87016?
No. The PostgreSQL code path uses an exact comparison. The flaw is on SQLite, which is the default backend.
- Is there an accidental form?
Yes. A normal subject containing an underscore can match several accounts, and the one returned depends on database row order.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology
More Open WebUI vulnerabilities
All Open WebUI entriesCVE-2026-87011
Open WebUI: Unauthenticated requests can stall the server via uncached OIDC fetches in back-channel logout
high7.5Sep 10, 2026CVE-2026-87012
Open WebUI: Any authenticated user can suppress calendar alerts instance-wide via a non-numeric alert value
medium4.3Sep 10, 2026CVE-2026-87013
Open WebUI: Any authenticated user can start a non-terminating request via a folder parent cycle
medium4.3Sep 10, 2026CVE-2026-87014
Open WebUI: Admin demoted through SSO role sync keeps read and write access to all users' notes
medium6.5Sep 10, 2026CVE-2026-87015
Open WebUI: A user's session cookies are sent to tool servers configured for bearer authentication
medium6.8Sep 10, 2026CVE-2026-87017
Open WebUI: Inaccessible knowledge bases are exposed through the built-in knowledge tool on most vector backends
medium4.3Sep 10, 2026
