Jul 18, 2026·7 min read·15 visits
A path traversal flaw in proot-distro prior to version 5.1.0 allows attackers to escape container directory limits and read or write arbitrary host files via the copy command.
A directory traversal vulnerability exists in the copy subcommand of the proot-distro utility. Due to incomplete path sanitization, local attackers or malicious scripts can read from or write to arbitrary files outside the container rootfs, bypassing isolation barriers and potentially gaining unauthorized access or persistent execution on the host system.
The proot-distro utility is a specialized tool used within rootless, proot-based environments—such as Termux on Android—to install and manage Linux distributions. To allow interaction between the host file system and the guest distributions, the utility provides a copy subcommand (copy or pd cp). This subcommand is designed to facilitate the secure transfer of files and configuration data across the logical partition of the container.
Because the host environment runs under a rootless profile, proot-distro uses a logical containment boundary rather than standard kernel-level namespace containment. The security model relies on path-resolution constraints to ensure that guest operations do not interact with host resources unless explicitly configured. The file transfer system uses user-supplied strings to specify target coordinates on both the host and the container.
When a user issues a copy command, the path parsing engine processes the arguments to establish canonical targets. However, the mechanism tasked with resolving container paths in vulnerable versions does not validate the logical scope of the output. This structural omission exposes an attack surface where arbitrary local filesystem reads and writes can be performed under the privilege set of the running utility context.
The root cause of the vulnerability resides in proot_distro/commands/copy.py (specifically within the path resolution helper function _resolve_copy_path). The application processes user inputs containing colons (formatted as distribution:path) by isolating the distribution identifier from the relative path. It utilizes Python's standard libraries to sanitize and normalize the target location.
To strip potential absolute paths that could reference host root resources directly, the utility invokes rel_path.lstrip("/") prior to combining it with the container's base directory. While this action effectively neutralizes absolute references (e.g., converting /etc/passwd into etc/passwd), it fails to address relative directory navigation structures. Specifically, sequence strings containing directory traversal operators (../) remain completely unaffected by the lstrip routine.
When the system executes os.path.join(rootfs, rel_path.lstrip("/")), the traversal sequences are appended directly to the container base directory path. The resulting path is then processed using os.path.normpath(). Because os.path.normpath resolves logical parent reference steps structurally, any excess of ../ sequences will walk backward up the directory hierarchy past the rootfs directory. Because the application did not verify whether the post-normalized path still retained the rootfs prefix, it accepted and acted upon paths that pointed to host files, leading to arbitrary reading and writing capabilities.
In vulnerable versions (v5.0.2 and earlier), path resolution was handled directly in the copy command module using insecure logical assumptions:
# Vulnerable Path Resolution Code (proot-distro <= v5.0.2)
def _resolve_copy_path(spec: str) -> str:
if ":" in spec:
dist, _, rel_path = spec.partition(":")
rootfs = os.path.join(CONTAINERS_DIR, dist, "rootfs")
if not os.path.isdir(rootfs):
sys.exit(1)
# BUG: lstrip("/") does not remove "../" traversal segments
# os.path.normpath resolves the traversals, escaping the rootfs boundaries
return os.path.normpath(os.path.join(rootfs, rel_path.lstrip("/")))
return os.path.normpath(os.path.abspath(spec))In the patched version (v5.1.0 and later), the path-resolution algorithm is relocated to proot_distro/paths.py and consolidated into resolve_container_path(spec). The remediated code introduces validation checks that strictly enforce boundary limitations:
# Patched Path Resolution Code (proot-distro >= v5.1.0)
def resolve_container_path(spec: str) -> str:
if ":" not in spec:
return os.path.normpath(os.path.abspath(spec))
name, _, rel_path = spec.partition(":")
if not is_valid_name(name):
crit_error(f"invalid container name '{name}' in spec '{spec}'.")
sys.exit(1)
rootfs = os.path.normpath(container_rootfs(name))
if not os.path.isdir(rootfs):
crit_error(f"container '{name}' does not exist.")
sys.exit(1)
# Normalize the combined paths first
resolved = os.path.normpath(os.path.join(rootfs, rel_path.lstrip("/")))
# FIX: Validate that the resolved path starts with the container rootfs path
# Incorporating os.sep prevents partial name matches (e.g. rootfs-alternate/)
if resolved != rootfs and not resolved.startswith(rootfs + os.sep):
crit_error("destination path escapes the container directory.")
sys.exit(1)
return resolvedThe introduction of resolved.startswith(rootfs + os.sep) acts as an effective boundary validation check. By checking against the rootfs path appended with the platform's directory separator, the code stops partial name match bypasses (such as escaping a directory named rootfs to access a sister folder named rootfs-data). This ensures complete validation and prevents paths that point outside the container's designated base directory.
An attacker within the local system context can exploit this directory traversal vulnerability to read or write arbitrary files on the host environment using the vulnerable copy logic.
To overwrite host-level configurations or files, the attacker specifies a relative traversal sequence in the target coordinate argument. The sequence traverses upwards from the rootfs directory into the host context:
# Overwrite a file on the host filesystem bypassing container containment
proot-distro copy \
~/poc/evil.txt \
"ubuntu:../../../../../../data/data/com.termux/files/home/poc/target.txt"An attacker can retrieve configuration data or private SSH keys from the host filesystem. This is done by specifying a traversal sequence in the source coordinate argument:
# Exfiltrate local keys via container file path resolution escape
proot-distro copy \
"ubuntu:../../../../../../data/data/com.termux/files/home/.ssh/id_rsa" \
~/poc/stolen_key.txtAn attacker can achieve persistent code execution on the host by overwriting shell initiation profiles like ~/.bashrc or ~/.profile. The next time the user spawns a terminal shell session on the host, the injected payload will execute with the user's host privileges:
# Inject command instructions into host .bashrc file
printf 'echo EXPLOIT_SUCCESSFUL > ~/poc/proof.txt\n' > ~/poc/payload.sh
proot-distro copy ~/poc/payload.sh \
"ubuntu:../../../../../../data/data/com.termux/files/home/.bashrc"The impact of this directory traversal vulnerability is rated as High. Because the container and host are run by the same local user account, the vulnerability does not escalate operating system-level permissions. However, it completely breaks the container boundary, bypassing isolation checks.
If a user downloads and executes an untrusted build script, script-based installer, or third-party automated container package, that script can act as a "confused deputy". The malicious code can use the vulnerability to read sensitive host files (such as SSH private keys, environment variables, authentication cookies, and database files) or modify critical configuration files. This allows the guest container to access host files that should remain isolated.
Furthermore, the ability to modify shell configuration files on the host allows an attacker to achieve persistent code execution. A containerized guest script can compromise the host Termux shell configuration, leading to automatic execution of arbitrary commands when a new shell is launched. This bypasses any isolation features that developers expect when running operations in a separate rootless container environment.
The primary remediation for this vulnerability is upgrading proot-distro to version 5.1.0 or later, which implements strict path validation.
Developers should use secure path resolution patterns when handling user input. It is critical to canonicalize paths using os.path.realpath or pathlib.Path.resolve to resolve symbolic links and traversal characters. After resolution, you must explicitly check that the target path begins with the absolute base directory path, as shown below:
from pathlib import Path
def safe_resolve_path(base_dir: Path, user_input: str) -> Path:
# Canonicalize base directory
resolved_base = base_dir.resolve(strict=True)
# Resolve target directory path
target_path = (resolved_base / user_input).resolve()
# Verify path boundaries
if not target_path.is_relative_to(resolved_base):
raise PermissionError("Directory traversal attempt detected.")
return target_pathUsing explicit validation checks like is_relative_to() or verifying prefix containment using directory separators prevents logical traversal vulnerabilities. This ensures that user-supplied input cannot access files outside the intended base directory.
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
proot-distro Termux | < 5.1.0 | 5.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Local |
| CVSS v3.1 Score | 7.3 |
| Exploit Status | PoC (Proof of Concept) |
| Impact | Arbitrary File Read & Write |
| KEV Status | Not Listed |
The product uses external input to construct a pathname that is intended to identify a file or directory that is located beneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVE-2026-12243 is a path traversal vulnerability in the Natural Language Toolkit (NLTK) version 3.9.4. The flaw exists because the input validation routine fails to account for percent-encoded directory traversal sequences like '..%2f' before passing them to urllib.request.url2pathname(), which decodes them into active traversal sequences.
CVE-2026-73654 is a high-severity prototype pollution vulnerability in Trigger.dev. The flaw occurs during the handling of run-metadata updates through the PUT /api/v1/runs/:runId/metadata endpoint. Because user-supplied keys are parsed directly by the @jsonhero/path library without sanitization, an authenticated attacker with low privileges can pollute the global Object.prototype. This causes database queries via Prisma ORM to fail validation and induces unhandled exceptions in the Prometheus metrics client, resulting in a process-wide denial of service.
CVE-2026-73559 is an uncontrolled resource consumption vulnerability in the vLLM engine, specifically within the /v1/completions API endpoint, allowing authenticated attackers to cause application-level denial of service via unbounded prompt arrays.
A critical security bypass vulnerability in Argo Workflows allows authenticated attackers with workflow submission privileges to bypass 'Strict' or 'Secure' template referencing restrictions. By injecting unvalidated fields into the nested ArtifactGC configuration, attackers can execute arbitrary pod patches, leading to host namespace escape and cluster-wide privilege escalation.
A Server-Side Request Forgery (SSRF) / Confused Deputy vulnerability has been identified in Pydantic AI UI Adapters (such as VercelAIAdapter). Under certain conditions, a malicious client can supply manipulated message history with provider metadata that forces the server to resolve files within privileged cloud environments (AWS S3, Google Cloud Storage) or model providers. This occurs because the adapters deserialize client-provided metadata structures directly into UploadedFile instances without validation, which are subsequently fetched using high-privilege server credentials.
A path traversal vulnerability in the optional dashboard server of atomic-agents-stack before version 1.1.0 allows unauthenticated remote attackers to read arbitrary files from the host filesystem.