{"id":"CVE-2026-109572","kind":"vulnerability","aliases":["CVE-2026-109572","GHSA-q46m-r89w-j74p"],"related":[],"title":"Terminal users can manage Terminals policies and other users' terminals via the terminal proxy","description":"# Terminal users can manage Terminals policies and other users' terminals via the terminal proxy\r\n\r\n## Summary\r\nA regular user who has been given access to a Terminals connection could send requests through Open WebUI's terminal proxy to the Terminals administrator API. Open WebUI forwarded any requested path and attached the administrator-configured shared key, so Terminals treated the request as an administrator request. The user could read and change terminal policies and list and stop other users' terminals, without ever seeing the key.\r\n\r\n## Preconditions\r\n- An administrator has added a Terminals (orchestrator) connection that uses bearer authentication (the default authentication type for terminal connections) with the Terminals API key.\r\n- The attacker has a normal verified account and has been granted access to that connection.\r\n- The connection is not pinned to a named policy. Connections pinned to a policy route requests into that policy's own terminal and do not reach the administrator API.\r\n- The connected Terminals server is older than 0.2.4. Terminals 0.2.4 refuses administrator requests that carry a user identity, which blocks this path independently.\r\n- Deployments without a Terminals connection are not affected.\r\n\r\n## Impact\r\nUnder the preconditions above the attack needs one authenticated request per action and no interaction from anyone else. The attacker could list all active terminals with their owners' user ids, policy, instance name, host, port and activity; stop another user's running terminal; read the Terminals status and configuration; and create and change terminal policies. Changing a policy was observed to take effect on other users: environment variables set by the attacker appeared in a second user's terminal when it next started on a connection pinned to that policy. Stopped terminals are started again on the next access. Open WebUI's own data is not exposed through this path.\r\n\r\n## Fix\r\nFixed in 51bb8cb142f72503e861eeee25ae4dc73c26c36b, released in 0.11.4. The terminal proxy now refuses to forward requests to the Terminals administrator paths (policies, status and terminal lifecycle). Terminals 0.2.4 additionally rejects administrator requests that arrive on behalf of a user.\r\n\r\n## Root cause\r\n- Component: `backend/open_webui/routers/terminals.py`, the `/api/v1/terminals/{server_id}/{path}` proxy.\r\n- Affected setup: every build from 0.8.6 through 0.11.3 with a Terminals connection configured.\r\n\r\nThe proxy was built to let users reach their own terminal through the orchestrator, and it forwarded whatever path the user asked for with the server's shared key attached. Terminals protects its administrator API with that same key, so the proxy never separated user traffic from administrator traffic and a user could choose an administrator path.\r\n\r\n## Proof of concept\r\nTested against Terminals 0.2.3 with the Docker backend and a bearer connection using the shared key, with two ordinary users. A direct request to Terminals without the key returns 401. Through Open WebUI 0.11.3, using only the attacker's own session:\r\n\r\n```\r\nGET  /api/v1/terminals/<server_id>/api/v1/policies                   -> 200\r\nPUT  /api/v1/terminals/<server_id>/api/v1/policies/poc-low-user      -> 200, policy created\r\nGET  /api/v1/terminals/<server_id>/api/v1/terminals                  -> 200, lists the victim's terminal\r\nPOST /api/v1/terminals/<server_id>/api/v1/terminals/stop\r\n     {\"user_id\":\"<victim>\",\"policy_id\":\"default\",\"context_id\":\"default\"}\r\n                                                                     -> 200 {\"matched\":1,\"stopped\":1,\"skipped_active\":0}\r\n```\r\n\r\nThe victim's terminal container was removed. Rewriting a policy with an `env` value and then having a second user open a connection pinned to that policy started a container carrying the attacker's value. On 0.11.4 every request above returns 403. The proxy was exercised directly with the connection configuration and user identity supplied by the test harness.\r\n\r\n## Credits\r\n- @dhlife09\r\n- @ye11oc4t\r\n- @jiwhan8655\r\n- @Dog-Parka","summary":"Open WebUI's terminal proxy forwarded any requested path to the Terminals orchestrator with the administrator's shared key attached, so a regular user with terminal access could read and change terminal policies and stop other users' terminals. An attacker can also set environment variables that appear in another user's terminal when it next starts.","severity":"high","cvss":7.6,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"open-webui","fixed":["0.11.4"],"affected":">= 0.8.6, < 0.11.4","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-862","open-webui","package","python","self-hosted","terminals"],"not_affected_if":["no-terminals-connection","open-webui-0.11.4-or-later","pinned-policy-connection"],"how_to_check":"- Check the installed Open WebUI version with `pip show open-webui` or the image tag; affected builds are `0.8.6` through `0.11.3`.\n- Check whether a Terminals connection is configured under the admin Terminals settings, using bearer authentication and no pinned policy.\n- Check the Terminals server version; it is affected below `0.2.4`.","fix":"- Open WebUI: move to `0.11.4` (fixed in commit 51bb8cb142f72503e861eeee25ae4dc73c26c36b).\n- Terminals: move to `0.2.4` or later, which rejects administrator requests that carry a user identity.","status":"open","published_at":"2026-09-28T16:36:41+00:00","first_seen_at":"2026-11-09T22:53:26+00:00","triaged_at":"2026-11-09T22:59:19+00:00","updated_at":"2026-11-09T22:59:19+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-09T22:59:19Z","model":"claude-haiku-5-5","output":{"fix":"- Open WebUI: move to `0.11.4` (fixed in commit 51bb8cb142f72503e861eeee25ae4dc73c26c36b).\n- Terminals: move to `0.2.4` or later, which rejects administrator requests that carry a user identity.","reach":"network","title":"Open WebUI: terminal proxy lets regular users reach Terminals admin API","impact":"medium","summary":"Open WebUI's terminal proxy forwarded any requested path to the Terminals orchestrator with the administrator's shared key attached, so a regular user with terminal access could read and change terminal policies and stop other users' terminals. An attacker can also set environment variables that appear in another user's terminal when it next starts.","audience":"developers","category":"package","nicknames":[],"who_cares":["open-webui","self-hosted","terminals","python"],"confidence":0.85,"fix_status":"available","needs_auth":"yes","worst_case":"regular user changes admin policies and stops other users' terminals","analyst_note":"","how_to_check":"- Check the installed Open WebUI version with `pip show open-webui` or the image tag; affected builds are `0.8.6` through `0.11.3`.\n- Check whether a Terminals connection is configured under the admin Terminals settings, using bearer authentication and no pinned policy.\n- Check the Terminals server version; it is affected below `0.2.4`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-terminals-connection","pinned-policy-connection","open-webui-0.11.4-or-later"],"who_is_affected":"Only deployments that run Open WebUI from `0.8.6` through `0.11.3` with a Terminals connection configured are exposed. Deployments without a Terminals connection, or with connections pinned to a named policy, are not reached through this path. Teams can skip it if they do not use Terminals.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000934,"input_tokens":5863,"output_tokens":696,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-109572':17A,25A '-2026':16A,24A '-862':30B '/api/v1/policies':555C '/api/v1/policies/poc-low-user':559C '/api/v1/terminals':434C,554C,558C,564C,565C,573C '/api/v1/terminals/stop':574C '0':590C '0.11.3':446C,545C '0.11.4':397C,627C '0.2.3':515C '0.2.4':256C,258C,417C '0.8.6':444C '1':585C,587C '200':556C,560C,566C,583C '401':541C '403':632C '51bb8cb142f72503e861eeee25ae4dc73c26c36b':394C 'access':66B,118C,216C,379C 'account':211C 'action':292C 'activ':304C,317C,589C 'add':184C 'addit':418C 'administr':55B,135C,147C,158C,182C,247C,260C,409C,420C,485C,499C,507C 'administrator-configur':146C 'affect':279C,439C 'also':81B 'anoth':88B,319C 'anyon':297C 'api':136C,203C,248C,486C 'appear':86B,353C 'arriv':423C 'ask':473C 'attach':59B,144C,481C 'attack':79B,206C,286C,300C,352C,549C,623C 'authent':192C,195C,289C 'backend':519C 'backend/open_webui/routers/terminals.py':432C 'bearer':191C,522C 'behalf':425C 'block':268C 'build':442C 'built':455C 'carri':263C,621C 'caus':430C 'chang':70B,165C,333C,336C 'choos':505C 'compon':431C 'concept':511C 'configur':148C,329C,451C,641C 'connect':122C,188C,199C,219C,221C,229C,250C,276C,366C,450C,523C,613C,640C 'contain':595C,620C 'context':580C 'could':67B,123C,162C,301C,504C 'creat':331C,562C 'cve':15A,23A 'cwe':29B 'data':384C 'default':194C,579C,582C 'deploy':272C 'direct':533C,637C 'docker':518C 'effect':343C 'els':298C 'env':603C 'environ':83B,347C 'ever':176C 'everi':441C,628C 'exercis':636C 'expos':387C 'fix':391C,392C 'forward':45B,139C,404C,468C 'get':553C,563C 'ghsa':19A 'ghsa-q46m-r89w-j74p':18A 'given':117C 'grant':215C 'har':649C 'host':38B,314C 'id':310C,436C,576C,578C,581C 'ident':266C,644C 'impact':280C 'independ':271C 'instanc':312C 'interact':295C 'j74p':22A 'key':58B,150C,179C,204C,480C,490C,527C,539C 'let':457C 'lifecycl':415C 'list':169C,302C,567C 'manag':4A,99C 'match':584C 'name':227C,313C 'need':287C 'never':494C 'next':94B,362C,378C 'normal':209C 'observ':340C 'older':254C 'one':288C 'open':27A,32B,40B,127C,137C,380C,543C,611C 'open-webui':26A,31B 'orchestr':52B,187C,465C 'ordinari':530C 'owner':308C 'packag':34B 'path':48B,142C,270C,390C,410C,437C,470C,508C 'per':291C 'pin':224C,230C,367C,614C 'polici':6A,72B,101C,167C,228C,233C,238C,311C,335C,338C,370C,411C,561C,577C,600C,617C 'port':315C 'post':572C 'precondit':180C,283C 'proof':509C 'protect':483C 'proxi':14A,44B,109C,131C,400C,438C,453C,493C,634C 'put':557C 'python':35B 'q46m':20A 'r89w':21A 'reach':245C,459C 'read':68B,163C,324C 'refus':259C,402C 'regular':62B,112C 'reject':419C 'releas':395C 'remov':597C 'request':47B,125C,141C,155C,159C,235C,261C,290C,405C,421C,534C,629C 'return':540C,631C 'rewrit':598C 'root':429C 'rout':234C 'run':322C 'second':356C,609C 'see':177C 'self':37B 'self-host':36B 'send':124C 'separ':495C 'server':252C,435C,477C 'session':552C 'set':82B,349C 'setup':440C 'share':57B,149C,479C,526C 'skip':588C 'start':95B,363C,374C,618C 'status':327C,412C 'stop':74B,171C,318C,371C,586C 'summari':110C 'suppli':645C 'take':342C 'termin':1A,5A,10A,13A,39B,43B,51B,65B,71B,77B,91B,96C,100C,105C,108C,121C,130C,134C,152C,166C,174C,186C,198C,202C,241C,251C,257C,275C,305C,323C,326C,334C,359C,372C,399C,408C,414C,416C,449C,462C,482C,514C,536C,571C,594C 'test':512C,648C 'traffic':497C,500C 'treat':153C 'two':529C 'type':196C 'use':190C,524C,546C 'user':2A,9A,63B,76B,89B,97C,104C,113C,161C,173C,265C,309C,320C,346C,357C,428C,458C,472C,496C,503C,531C,575C,610C,643C 'valu':604C,625C 'variabl':84B,348C 'verifi':210C 'via':11A,106C 'victim':569C,592C 'webui':28A,33B,41B,128C,138C,381C,544C 'whatev':469C 'without':175C,273C,537C","nicknames":[],"mitigations":[],"who_affected":"Only deployments that run Open WebUI from `0.8.6` through `0.11.3` with a Terminals connection configured are exposed. Deployments without a Terminals connection, or with connections pinned to a named policy, are not reached through this path. Teams can skip it if they do not use Terminals.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["openwebui"],"ptokens":["open","webui"],"labels":["cwe862","high","openwebui","package","pypi","python","selfhosted","terminals"],"campaign_type":null,"search_a":"'-109572':17A,25A '-2026':16A,24A 'cve':15A,23A 'ghsa':19A 'ghsa-q46m-r89w-j74p':18A 'j74p':22A 'manag':4A 'open':27A 'open-webui':26A 'polici':6A 'proxi':14A 'q46m':20A 'r89w':21A 'termin':1A,5A,10A,13A 'user':2A,9A 'via':11A 'webui':28A","search_b":"'-862':30B 'access':66B 'administr':55B 'also':81B 'anoth':88B 'appear':86B 'attach':59B 'attack':79B 'chang':70B 'could':67B 'cwe':29B 'environ':83B 'forward':45B 'host':38B 'key':58B 'next':94B 'open':32B,40B 'open-webui':31B 'orchestr':52B 'packag':34B 'path':48B 'polici':72B 'proxi':44B 'python':35B 'read':68B 'regular':62B 'request':47B 'self':37B 'self-host':36B 'set':82B 'share':57B 'start':95B 'stop':74B 'termin':39B,43B,51B,65B,71B,77B,91B 'user':63B,76B,89B 'variabl':84B 'webui':33B,41B","search_c":"'/api/v1/policies':555C '/api/v1/policies/poc-low-user':559C '/api/v1/terminals':434C,554C,558C,564C,565C,573C '/api/v1/terminals/stop':574C '0':590C '0.11.3':446C,545C '0.11.4':397C,627C '0.2.3':515C '0.2.4':256C,258C,417C '0.8.6':444C '1':585C,587C '200':556C,560C,566C,583C '401':541C '403':632C '51bb8cb142f72503e861eeee25ae4dc73c26c36b':394C 'access':118C,216C,379C 'account':211C 'action':292C 'activ':304C,317C,589C 'add':184C 'addit':418C 'administr':135C,147C,158C,182C,247C,260C,409C,420C,485C,499C,507C 'administrator-configur':146C 'affect':279C,439C 'anoth':319C 'anyon':297C 'api':136C,203C,248C,486C 'appear':353C 'arriv':423C 'ask':473C 'attach':144C,481C 'attack':206C,286C,300C,352C,549C,623C 'authent':192C,195C,289C 'backend':519C 'backend/open_webui/routers/terminals.py':432C 'bearer':191C,522C 'behalf':425C 'block':268C 'build':442C 'built':455C 'carri':263C,621C 'caus':430C 'chang':165C,333C,336C 'choos':505C 'compon':431C 'concept':511C 'configur':148C,329C,451C,641C 'connect':122C,188C,199C,219C,221C,229C,250C,276C,366C,450C,523C,613C,640C 'contain':595C,620C 'context':580C 'could':123C,162C,301C,504C 'creat':331C,562C 'data':384C 'default':194C,579C,582C 'deploy':272C 'direct':533C,637C 'docker':518C 'effect':343C 'els':298C 'env':603C 'environ':347C 'ever':176C 'everi':441C,628C 'exercis':636C 'expos':387C 'fix':391C,392C 'forward':139C,404C,468C 'get':553C,563C 'given':117C 'grant':215C 'har':649C 'host':314C 'id':310C,436C,576C,578C,581C 'ident':266C,644C 'impact':280C 'independ':271C 'instanc':312C 'interact':295C 'key':150C,179C,204C,480C,490C,527C,539C 'let':457C 'lifecycl':415C 'list':169C,302C,567C 'manag':99C 'match':584C 'name':227C,313C 'need':287C 'never':494C 'next':362C,378C 'normal':209C 'observ':340C 'older':254C 'one':288C 'open':127C,137C,380C,543C,611C 'orchestr':187C,465C 'ordinari':530C 'owner':308C 'path':142C,270C,390C,410C,437C,470C,508C 'per':291C 'pin':224C,230C,367C,614C 'polici':101C,167C,228C,233C,238C,311C,335C,338C,370C,411C,561C,577C,600C,617C 'port':315C 'post':572C 'precondit':180C,283C 'proof':509C 'protect':483C 'proxi':109C,131C,400C,438C,453C,493C,634C 'put':557C 'reach':245C,459C 'read':163C,324C 'refus':259C,402C 'regular':112C 'reject':419C 'releas':395C 'remov':597C 'request':125C,141C,155C,159C,235C,261C,290C,405C,421C,534C,629C 'return':540C,631C 'rewrit':598C 'root':429C 'rout':234C 'run':322C 'second':356C,609C 'see':177C 'send':124C 'separ':495C 'server':252C,435C,477C 'session':552C 'set':349C 'setup':440C 'share':149C,479C,526C 'skip':588C 'start':363C,374C,618C 'status':327C,412C 'stop':171C,318C,371C,586C 'summari':110C 'suppli':645C 'take':342C 'termin':96C,100C,105C,108C,121C,130C,134C,152C,166C,174C,186C,198C,202C,241C,251C,257C,275C,305C,323C,326C,334C,359C,372C,399C,408C,414C,416C,449C,462C,482C,514C,536C,571C,594C 'test':512C,648C 'traffic':497C,500C 'treat':153C 'two':529C 'type':196C 'use':190C,524C,546C 'user':97C,104C,113C,161C,173C,265C,309C,320C,346C,357C,428C,458C,472C,496C,503C,531C,575C,610C,643C 'valu':604C,625C 'variabl':348C 'verifi':210C 'via':106C 'victim':569C,592C 'webui':128C,138C,381C,544C 'whatev':469C 'without':175C,273C,537C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-09-28T16:36:41+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"disclosure@vulncheck.com","poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-q46m-r89w-j74p","url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-q46m-r89w-j74p","severity":"high","published_at":"2026-09-28T16:36:41+00:00","modified_at":"2026-10-28T20:47:19+00:00","first_seen_at":"2026-11-09T22:53:26+00:00","fetched_at":"2026-11-09T22:53:26+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-109572","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-109572","severity":null,"published_at":"2026-10-12T23:17:22+00:00","modified_at":"2026-10-13T20:39:48+00:00","first_seen_at":"2026-11-09T23:10:12+00:00","fetched_at":"2026-11-09T23:10:12+00:00","note":null}],"documents":[{"id":110644,"url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-q46m-r89w-j74p","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":"2026-09-28T16:36:41+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":111045,"url":"https://github.com/open-webui/open-webui/commit/51bb8cb142f72503e861eeee25ae4dc73c26c36b","title":"Patch: github.com","kind":"other","source":"nvd","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":111047,"url":"https://www.vulncheck.com/advisories/open-webui-0.8.6-before-0.11.4-privilege-escalation-via-terminal-proxy","title":"Third-party advisory: www.vulncheck.com","kind":"advisory","source":"nvd","publisher":"research","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Third-party advisory: www.vulncheck.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}