Skip to main content

Open WebUI CVE-2026-54017

HIGH
Path Traversal (CWE-22)
2026-06-17 https://github.com/open-webui/open-webui GHSA-r2wg-2mcr-66rv
7.7
CVSS 3.1 · Vendor: https://github.com/open-webui/open-webui
Share

Severity by source

Vendor (https://github.com/open-webui/open-webui) PRIMARY
7.7 HIGH
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
vuln.today AI
8.5 HIGH

Network-reachable proxy, low complexity, authenticated low-privilege grant required (PR:L), scope change to upstream terminal server, high confidentiality and low integrity since traversal can also reach write-capable upstream endpoints.

3.1 AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N

Primary rating from Vendor (https://github.com/open-webui/open-webui).

CVSS VectorVendor: https://github.com/open-webui/open-webui

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
None
Availability
None

Lifecycle Timeline

2
Source Code Evidence Fetched
Jun 18, 2026 - 01:33 vuln.today
Analysis Generated
Jun 18, 2026 - 01:33 vuln.today

DescriptionCVE.org

Summary

The terminal-server reverse proxy in backend/open_webui/routers/terminals.py does not fully confine the user-controlled path segment before forwarding it to an admin-configured terminal server. An authenticated user who has been granted access to a terminal server can craft path values containing encoded ../ traversal sequences that escape the intended path (or policy) scope on that server, reaching unintended endpoints and files on the terminal-server host. Where the terminal server fans requests out to internal services, this also gives SSRF-style reach into those services.

This is a separate code path from the /api/v1/retrieval/process/web SSRF (GHSA-c6xv-rcvw-v685), with its own input. Two distinct vectors are consolidated here:

  1. Raw path forwarding / single-encoded traversal (original report).
  2. A bypass of the subsequently-added _sanitize_proxy_path mitigation using double-encoded dots (%252e%252e).

The attacker-controlled input is the request path, supplied by the non-admin user, not anything an administrator configures, so this is not an admin-trust / Rule-9 situation.

Affected code

The proxy route forwards an arbitrary trailing path to the configured terminal server:

python
# routers/terminals.py
@router.api_route('/{server_id}/{path:path}', methods=PROXY_METHODS)
async def proxy_terminal(server_id, path, request, user=Depends(get_verified_user)):
    ...
    safe_path = _sanitize_proxy_path(path)
    if safe_path is None:
        return JSONResponse({'error': 'Invalid path'}, status_code=400)
    target_url = f'{base_url}/{safe_path}'
    policy_id = connection.get('policy_id')
    if policy_id:
        target_url = f'{base_url}/p/{policy_id}/{safe_path}'

Access requires has_connection_access(user, connection, ...), i.e. a non-admin user the administrator has granted to that terminal server.

Vector 1 - single-encoded traversal (original)

The path was originally concatenated to the base URL with no sanitization (target_url = f"{base_url}/{path}"), so single-encoded traversal escaped the intended scope:

GET /api/v1/terminals/server1/..%2F..%2F..%2Finternal-api/secrets
# proxied to: {base_url}/../../../internal-api/secrets

This vector is closed at HEAD: _sanitize_proxy_path now URL-decodes once, runs posixpath.normpath, strips leading slashes, and rejects results beginning with .. (unquote('..%2F..%2F') -> '../../' -> normpath -> '../..' -> rejected).

Vector 2 - double-encoded bypass of _sanitize_proxy_path

_sanitize_proxy_path decodes the path only once before the .. check, so a double-encoded payload survives:

python
def _sanitize_proxy_path(path: str) -> str | None:
    decoded = unquote(path)
# single decode pass only
    normalized = posixpath.normpath(decoded)
    cleaned = normalized.lstrip('/')
    if cleaned.startswith('..') or cleaned == '.':
        return None
    ...

unquote('%252e%252e/secret') yields %2e%2e/secret (not ..), which normpath leaves unchanged and which does not start with .., so it passes the check. The proxy then forwards {base_url}/%2e%2e/secret, and the upstream terminal server decodes %2e%2e into .. and resolves the traversal the check was meant to prevent.

GET /api/v1/terminals/server1/%252e%252e/%252e%252e/sensitive-file
# passes _sanitize_proxy_path as %2e%2e/%2e%2e/sensitive-file
# upstream decodes -> ../../sensitive-file

The policy_id form ({base_url}/p/{policy_id}/{safe_path}) is the higher-impact target: traversal escapes the policy namespace and reaches other policies or the terminal-server root.

Impact

An authenticated user with access to a terminal server can escape the intended path/policy scope on that server, reaching unintended endpoints and files, and, where the terminal server routes onward to internal services, reach those services. CWE-22 (Path Traversal) and CWE-918 (SSRF).

Fix

Decode the proxy path until it is stable before normalising and checking, so no depth of encoding can smuggle a traversal sequence past the check to be re-decoded upstream:

python
decoded = path
for _ in range(8):
    once = unquote(decoded)
    if once == decoded:
        break
    decoded = once
normalized = posixpath.normpath(decoded)
cleaned = normalized.lstrip('/')
if cleaned.startswith('..') or cleaned == '.':
    return None

This rejects %2e%2e, %252e%252e, %25252e%25252e, ..%2f..%2f, etc., while leaving legitimate paths (including singly-encoded characters such as %20) intact.

Credits

  • Tulgaaaaaaaa - original report (terminal-proxy path SSRF / single-encoded traversal).
  • sermikr0 - double-encoded (%252e%252e) bypass of the _sanitize_proxy_path mitigation.

AnalysisAI

Authenticated path traversal and SSRF in Open WebUI's terminal-server reverse proxy (versions <= 0.9.5) allows non-admin users with granted terminal access to escape the intended path or policy scope using double-encoded traversal sequences (%252e%252e). The flaw bypasses the project's own _sanitize_proxy_path mitigation, which performs only a single URL-decode pass before the '..' check, letting attackers reach unintended endpoints, files, and internal services routed by the terminal server. No public exploit identified at time of analysis, though the vendor advisory includes working proof-of-concept request strings.

Technical ContextAI

The vulnerability resides in backend/open_webui/routers/terminals.py within Open WebUI, a Python (pip/open-webui) self-hosted LLM frontend. The proxy_terminal FastAPI route forwards an attacker-supplied {path:path} segment to an admin-configured upstream terminal server, optionally namespaced under a policy_id. The mitigation _sanitize_proxy_path calls unquote() exactly once, then posixpath.normpath, then rejects results beginning with '..'. Because unquote is single-pass, an input like %252e%252e decodes to literal %2e%2e (not ..), passes the check, and is then re-decoded by the upstream HTTP server into the actual '..' traversal. This is a classic CWE-22 (Path Traversal) compounded by CWE-918 (SSRF) where the proxy boundary becomes a pivot point into the terminal-server host and any internal services it fans out to.

RemediationAI

Vendor-released patch: upgrade Open WebUI to 0.9.6 or later, where _sanitize_proxy_path now loops unquote up to eight times until the string stabilizes before normpath and the '..' check, neutralizing %2e%2e, %252e%252e, %25252e%25252e, and mixed forms like ..%2f..%2f. Until the upgrade is rolled out, restrict or revoke non-admin grants on configured terminal servers via has_connection_access, or temporarily disable terminal-server connections entirely (side effect: terminal feature unavailable to all users); a network-layer ACL on the upstream terminal server limiting it to expected paths or policies is also viable but operationally fragile. Reference: https://github.com/open-webui/open-webui/security/advisories/GHSA-r2wg-2mcr-66rv.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-49869 CRITICAL POC
10.0 Jun 26

Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

Share

CVE-2026-54017 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy