mtzanidakis/praktor · Security

praktor: Path traversal in Telegram file uploads allows writing outside the agent workspace uploads/ directory (stored instruction injection)

GHSA ID
GHSA-48v2-6jcm-fc99
CVE ID
CVE-2026-62331
Severity
high
Published
Updated
CVSS
8.2 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
CVSS v3
8.2 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N

Description

Impact

A remote Telegram user can write files to arbitrary paths inside an agent's persistent workspace volume (praktor-wk-<workspace>, mounted at /workspace/agent), escaping the documented uploads/ directory.

The attacker controls the Telegram document filename, which was concatenated into the destination path without sanitization (internal/telegram/bot.go, at both upload sites) and resolved with path.Join, which collapses ../ sequences. A filename like ../../../CLAUDE.md resolved to the root of the agent workspace volume, outside uploads/.

Because the agent workspace is the persistent working directory the Claude agent reads from and operates on across runs, writing outside uploads/ lets an attacker plant or overwrite files the agent later consumes — e.g. CLAUDE.md, agent instruction/skill/hook files, or other config under /workspace/agent. This turns a file upload into a stored injection into the agent's instruction/execution surface (CWE-22 path traversal escalating to stored instruction injection).

Reachability

By default the bot has no user allowlist (allow_from: [] means "allow all"), so the only precondition is that a user can message the bot. No authentication or prior privilege is required.

Affected versions

Through v0.9.0 (and HEAD 3902d29).

Patched versions

v0.9.1, containing PR #228.

Workarounds

Restrict telegram.allow_from to trusted user IDs to limit who can send uploads to the bot. Upgrading to v0.9.1 is the recommended fix.

Fix (defense in depth)

  • internal/telegram/bot.go: apply path.Base() to the attacker-controlled filename at both upload sites, stripping directory components and ...
  • internal/container/manager.go: WriteVolumeBytes now rejects any path that escapes /vol after path.Join, protecting future callers.

Credit

Reported privately by tonghuaroot, who provided a detailed root-cause analysis, a deployed end-to-end proof of concept against the real container.Manager, and fix verification.

CWEs

References