Any authenticated user can permanently delete files owned by other users via DELETE /api/v1/files/{id} when the target file is referenced in any shared chat. The has_access_to_file() authorization gate unconditionally grants access through its shared-chat branch. It checks neither the requesting user's identity nor the type of operation being performed. File UUIDs (which would otherwise be impractical to guess) are disclosed to any user with read access to a knowledge base via GET /api/v1/knowledge/{id}/files.
The root cause is in has_access_to_file() in backend/open_webui/routers/files.py.
When a user calls DELETE /api/v1/files/{file_id}, the endpoint delegates authorization to has_access_to_file(file_id, access_type="write", user=requesting_user). Inside that function, one branch checks whether the file is referenced in any shared chat:
chats = Chats.get_shared_chats_by_file_id(file_id, db=db)
if chats:
return True
This branch has two missing checks:
access_type parameter ("write" for delete) is accepted but never inspected. The branch returns True regardless of whether the caller is requesting read access or delete access.The result: if any user has shared any chat that references a file, that file becomes deletable by every authenticated user on the instance.
The delete endpoint has no secondary ownership check (unlike the content-update endpoint), so this authorization bypass leads directly to permanent file removal from the database, disk, and all knowledge base associations.
How an attacker obtains file UUIDs:
UUIDs are impractical to brute-force, but they don't need to be. Any...
0.1.1240.1.1250.2.00.2.10.2.20.2.30.2.40.2.50.3.00.3.1+141 more0.9.0Exploitability
AV:NAC:LPR:LUI:RScope
S:UImpact
C:HI:HA:HCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H