[Eclipse Theia] Cross-Origin WebSocket Access To Shell-Terminal Enables Command Execution And Output Exfiltration
From the security Mailing List: # Cross-Origin WebSocket Access To Shell-Terminal Enables Command Execution And Output Exfiltration ## Executive Summary The browser backend exposes privileged terminal RPC over the shared Socket.IO namespace (`/services`) without a terminal-specific authentication/authorization gate. A foreign-origin webpage can open the `/services/shell-terminal` channel, invoke terminal creation, connect to `/services/terminals/<id>`, execute commands, and read command output. I verified this non-destructively by executing `id` and extracting `uid=...` output from the terminal stream in a real browser. On the tested checkout, this worked in both: - default mode (without `THEIA_HOSTS`), and - the script's `THEIA_HOSTS=<host:port>` phase. ## Product / Asset - Product: Eclipse Theia browser backend with `@theia/terminal`. - Transport: Socket.IO namespace `/services`. - Privileged channel: `/services/shell-terminal`. - Terminal data channel: `/services/terminals/:id`. ## Suggested Severity - Suggested severity: High (potentially Critical depending on deployment exposure). - CWE: CWE-306 (Missing Authentication for Critical Function), CWE-1385 (Missing Origin Validation in WebSockets). - CVSS v3.1 (drive-by browser case): `AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H`. ## Why This Matters - Local developer setup risk: a user running Theia locally can be compromised by visiting a malicious webpage. - Public deployment risk: if the backend is exposed and upstream auth is weak/misconfigured, this becomes direct remote command execution. ## High-Confidence Evidence - Reproduction uses a real browser from a foreign origin. - No Node-only headers are required by the PoC page. - PoC executes `id` and extracts actual terminal output (`uid=...`) end-to-end. - Reproduction is non-destructive (no file write or persistence payload required). ## Affected Code Paths - `packages/terminal/src/node/terminal-backend-module.ts` - Registers `/services/shell-terminal` RPC handler with no authz gate. - `packages/terminal/src/node/shell-terminal-server.ts` - `create(options)` accepts remote-controlled options and spawns shell process. - `packages/terminal/src/node/shell-process.ts` - Terminal options flow into command/args/cwd/env. - `packages/process/src/node/terminal-process.ts` - Executes sink via `node-pty.spawn(...)`. - `packages/core/src/node/messaging/default-messaging-service.ts` - Shared `/services` multiplex transport. - `packages/core/src/common/message-rpc/channel.ts` - Logical channel open protocol (`Open`, `AckOpen`, `Data`). - `packages/core/src/node/hosting/ws-origin-validator.ts` - Fail-open when `Origin` is missing. - `packages/core/src/node/messaging/websocket-endpoint.ts` - Overwrites `request.headers.origin` with optional `fix-origin` header before validation. - `packages/core/README.md` - Documents `THEIA_HOSTS` as WebSocket origin control and states any-origin behavior when unset. ## Reproduction (Maintainer Quick Path) ### Tested Environment - Repository: `eclipse-theia/theia` - Tested commit: `6e4fccbd9f69886b679e3d34ef27218e87885659` - Date: 2026-03-11 ### One-Command Repro From repository workspace root (the directory containing `safe_full_app_cross_origin_demo.sh`):  ⁠bash THEIA_PORT=51423 ATTACK_PORT=51424 DEMO_INTERACTIVE=0 KEEP_RUNNING=0 bash safe_full_app_cross_origin_demo.sh ### What The Script Does 1. Starts Theia browser example on `THEIA_PORT`. 2. Serves attacker page on different origin (`ATTACK_PORT`). 3. Foreign-origin page connects to `/services`, performs `initialConnection`, opens `/services/shell-terminal`, invokes `create`, opens `/services/terminals/<id>`, sends `id`, and reads output. 4. Repeats in a second phase with `THEIA_HOSTS=<THEIA_HOST>:<THEIA_PORT>` set by the script. ### Success Signals The attacker page log includes: - `ack-open` - `create-request:1` - `reply:<terminalId>` - `terminal-open-ok` - `id-dispatched` - `id-output:uid=...` - `id-output-ok` Script-level success requires `id-output-ok` in both phases. ## Expected vs Actual ### Expected - Untrusted foreign-origin clients cannot reach privileged terminal RPC. - With host restrictions enabled, cross-origin websocket attempts should be denied in browser deployments. - Terminal creation should require authenticated/authorized principal. ### Actual - Foreign-origin page executes `id` and exfiltrates output through terminal channel. - On tested checkout, exploit succeeds in both default and `THEIA_HOSTS` phase. ## Root Cause Analysis 1. Privileged RPC (`/services/shell-terminal`) assumes transport reachability equals trust. 2. `create()` allows caller-controlled process options into PTY spawn path. 3. WebSocket origin admission is optional/fail-open in some paths (missing `Origin` accepted). 4. The endpoint rewrites origin from optional `fix-origin`, which weakens guarantees if absent/unreliable. ## Impact Scenarios - Drive-by local compromise: attacker page triggers command execution as victim user while Theia runs locally. - Hosted/tunneled misconfiguration: direct remote command execution if backend websocket is reachable without strong external auth. - Post-exploitation capability: read/write repository files, access secrets in environment, alter build outputs. ## Recommended Remediation 1. Enforce authn/authz at terminal RPC service boundary (do not trust network reachability alone). 2. Make websocket admission fail-closed for browser target: - reject missing/invalid origin, - avoid replacing trusted origin context with user-supplied fallback headers, - require strict host allowlist matching. 3. Bind terminal creation/attach to authenticated session principals and explicit permissions. 4. Add defense-in-depth runtime controls (disable terminal service when not needed, least privilege process environment). [exploit.sh](/uploads/f08e40a8573acc3cfcc31950f528930f/exploit.sh)
issue

Copyright © Eclipse Foundation AISBL. All rights reserved.     Privacy Policy | Terms of Use | Copyright Agent