GitHub Security Advisory: GHSA-3x8w-4f7p-xxc2
Package: open-webui (PIP)
Severity: HIGH (CVSS 8.7)
CWE: CWE-668: Exposure of Resource to Wrong Sphere
Vulnerable versions: <= 0.8.12
Patched in: 0.9.0

Summary: Open WebUI: Redis Cache Keys tool_servers and terminal_servers Missing Instance Prefix Enable Cross-Instance Cache Poisoning

Details:
# Redis Cache Keys tool_servers and terminal_servers Missing Instance Prefix Enable Cross-Instance Cache Poisoning

## Affected Component

Tool server and terminal server Redis cache:
- `backend/open_webui/utils/tools.py` (line 841, tool_servers SET)
- `backend/open_webui/utils/tools.py` (line 850, tool_servers GET)
- `backend/open_webui/utils/tools.py` (line 976, terminal_servers SET)
- `backend/open_webui/utils/tools.py` (line 986, terminal_servers GET)

## Affected Versions

Current main branch (commit `6fdd19bf1`) and likely all versions since the tool server / terminal server Redis cache was introduced.

## Description

Open WebUI uses a `REDIS_KEY_PREFIX` (default `open-webui`) to namespace Redis keys, allowing multiple instances to safely share a single Redis backend. Every Redis key in the codebase uses this prefix — except the `tool_servers` and `terminal_servers` keys in `utils/tools.py`, which use bare key names.

When two or more Open WebUI instances share a Redis database (a supported and documented deployment pattern, e.g., for multi-region deployments, blue-green setups, or cluster topologies), the unprefixed keys collide. An admin on Instance A writing to `tool_servers` overwrites the value read by Instance B — causing Instance B's users to receive Instance A's tool server configuration.

```python
# utils/tools.py — unprefixed keys (problem)
await request.app.state.redis.set('tool_servers', ...)        # line 841
json.loads(await request.app.state.redis.get('tool_servers')) # line 850
await request.app.state.redis.set('terminal_servers', ...)    # line 976
json.loads(await request.app.state.redis.get('terminal_servers'))  # line 986

# Every other Redis key in the codebase — prefixed (correct pattern)
f'{REDIS_KEY_PREFIX}:auth:token:{jti}:revoked'
f'{REDIS_KEY_PREFIX}:ratelimit:{email}:{bucket}'
f'{REDIS_KEY_PREFIX}:tasks:commands'
```

## Attack Scenario

Two Open WebUI instances (A and B) share a Redis backend — a supported deployment for multi-region setups, blue-green deployments, or hot-standby. Both instances have their own admin accounts; the shared Redis was chosen for coordinated session handling, rate limiting, and task management.

1. Attacker is an admin on Instance A (a legitimately provisioned admin, or one that escalated via any available path including the LDAP empty-password or stale-admin-role findings).
2. Attacker on Instance A configures a tool server pointing to `https://attacker-controlled.example.com/openapi.json`. This triggers `utils/tools.py:841` to write the new tool server list under the bare key `tool_servers`.
3. Instance B's users query tools. Instance B reads from `tool_servers` (line 850) — gets Instance A's poisoned list, which now includes the attacker's server alongside or instead of Instance B's legitimate tool servers.
4. Instance B's users invoke tools through the model's context. The attacker's server receives tool call payloads containing: chat content, user identity, OAuth tokens scoped