# Open WebUI's process_files_batch() endpoint missing ownership check, allows unauthorized file overwrite

Open WebUI's batch file-processing endpoint lacks ownership checks, letting any authenticated user overwrite arbitrary files by UUID and poison RAG responses.

- Payout: Research
- Severity: high
- Program: Open WebUI
- Found by: Inar1Dev
- Disclosed: 2026-03-27
- CVE: CVE-2026-28788
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-jjp7-g2jw-wh3j
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-jjp7-g2jw-wh3j
- Tags: area:ai domain:ai domain:web impact:integrity kind:research platform:ghsa sev:high tech:llm tech:python technique:source-review vuln:access-control vuln:idor vuln:prompt-injection

**Target:** POST /api/v1/retrieval/process/files/batch endpoint in retrieval.py
**Root cause:** process_files_batch() was designed as an internal helper called by the knowledge router after its own access checks, but is also exposed as a standalone HTTP route requiring only get_verified_user, with no file.user_id == user.id verification before Files.update_file_by_id().
**Steps:**
1. Authenticate as a regular user with read access to a shared knowledge base.
2. Enumerate file UUIDs via `GET /api/v1/knowledge/{kb_id}/files`.
3. Send `POST /api/v1/retrieval/process/files/batch` with attacker-controlled `content` for the target file ID.
4. Confirm overwrite by reading the file back; content hash and data are replaced.
5. Query the knowledge base in chat as another user to observe the injected canary string in the LLM response.
**How found:** Noticed process_files_batch() is imported and called by knowledge.py after its own ownership checks, but is also decorated as a standalone @router.post route guarded only by get_verified_user; the frontend never calls it directly, revealing the missing check on the exposed path.
**Exploitation:** Attacker with any authenticated account and read access to a shared KB harvests file UUIDs, then posts batch file updates with arbitrary content to the retrieval endpoint, silently replacing the original document. The poisoned content is served to the LLM via RAG, letting the attacker control model output for all users of that KB, including injected instructions.
**Escalation:** Read-only KB access escalates to write over any file in the KB; RAG poisoning can inject instructions the model follows, potentially chaining to code interpreter or function-calling capabilities.
**Insight:** When a helper function is both imported internally and exposed as a route, audit the route's own authz separately from the caller's — internal callers' checks don't protect the direct endpoint. Grep for @router decorators on functions also imported elsewhere.

## Proof of concept (copied exactly from the original report)

**Exploit command overwriting target file by UUID**

```
python3 poc_exploit.py --url http://<host>:3000 --file-id <target-file-uuid> -t <attacker-jwt>

The PoC script: [poc_exploit.py](https://github.com/user-attachments/files/25470374/poc_exploit.py)
1. Authenticates as the attacker
2. Overwrites the target file via `POST /api/v1/retrieval/process/files/batch`
```

**Request listing file UUIDs from knowledge base**

```
GET /api/v1/knowledge/{kb_id}/files

This returns metadata for all files in the KB, including their UUIDs.
```

