GHSA-9xq3-3fqg-4vg7 `proot-distro install` has a Symlink Escape (Arbitrary Host File Write) via Malicious Tar Archive
**Repository:** `termux/proot-distro`  
**Component:** `proot_distro/commands/install.py` → `_extract_plain_tar()`; also `helpers/docker.py` → `_apply_layer()`Affected Versions| Component     | Version                      |
|---------------|------------------------------|
| proot-distro  | 5.0.2 (confirmed vulnerable) |
| Termux app    | 0.119.0-beta.3               |
| Device / ABI  | Samsung Galaxy A23 / aarch64 |
| Python (host) | 3.13                         |Vulnerability Description`proot-distro install` extracts a plain tarball rootfs by calling `_extract_plain_tar()` in
`proot_distro/commands/install.py`. This function correctly rejects tar member **names**
containing `..` components, but applies **no equivalent check on symlink targets**
(`member.linkname`). A tar archive can therefore:Plant a symlink inside the rootfs whose target is an **absolute host path**
(e.g. `/data/data/com.termux/files/home`).Write a subsequent regular-file member whose path traverses through that symlink name.Python's `open()` follows the symlink, writing the file **on the host filesystem** at the
privilege level of the Termux process — entirely during `proot-distro install`, before the
container is ever run.The same `_extract_plain_tar` function is reachable via `proot-distro reset`, and the
equivalent `_apply_layer` in `helpers/docker.py` contains the same flaw.Vulnerable Code**`proot_distro/commands/install.py`**, `_extract_plain_tar()`:The existing traversal guard only covers member **names**:There is **no check on `member.linkname`**. An absolute symlink target bypasses this guard
entirely.| Check                              | Member name (`rel_path`) | Symlink target (`member.linkname`) |
|------------------------------------|:------------------------:|:----------------------------------:|
| Reject `..` components             | ✅                        | ❌                                  |
| Confirm stays inside `rootfs_dir`  | ❌                        | ❌                                  |Proof of ConceptStep 1 — Craft a malicious archiveStep 2 — Install the archiveStep 3 — Verify proot-distro versionStep 4 — Verify host write`POC_SUCCESS` was written directly into the host Termux `$HOME` during installation,
with no container login required.Impact**Arbitrary host file write** at the full privilege of the Termux process, triggered solely
by `proot-distro install` — no container interaction required.A malicious `.tar.gz` / `.tar.xz` / `.tgz` archive delivered via a file download, CI
artifact, or compromised mirror is sufficient to exploit this.Practical payloads include overwriting `~/.bashrc`, `~/.profile`,
`$PREFIX/etc/bash.bashrc`, or any file in the Termux home/prefix, achieving **persistent
code execution** the next time the user opens a shell.Also reachable via `proot-distro reset` if the malicious archive is reused, and via
`_apply_layer` in `helpers/docker.py`.Root Cause`_extract_plain_tar` validates member **names** against `..` traversal but places no
restriction on symlink **targets**. The two vectors are treated asymmetrically:| Check                              | Member name (`rel_path`) | Symlink target (`member.linkname`) |
|------------------------------------|:------------------------:|:----------------------------------:|
| Reject `..` components             | ✅                        | ❌                                  |
| Confirm stays inside `rootfs_dir`  | ❌                        | ❌                                  |The member-name check (`any(p in ('..', '') for p in rel_parts)`) is necessary but not
sufficient: a symlink with an absolute target bypasses it entirely.Proposed FixAdd a guard that rejects any symlink whose resolved target falls outside `rootfs_dir`.
Apply in both `_extract_plain_tar` and `_apply_layer`:Then in `_extract_plain_tar`:Apply the identical guard inside `_apply_layer` in `helpers/docker.py`.**Note:** A stricter alternative — matching Docker's own behavior — is to **rewrite**
absolute symlink targets to relative paths within the rootfs rather than dropping them,
to avoid breaking legitimate images that use absolute intra-rootfs symlinks such as
`/usr/lib → /lib`.*Confirmed on proot-distro 5.0.2, Termux on Android/aarch64.*  
*Maintainer: @sylirre — report via GitHub Security Advisory on the `termux/proot-distro` repository.*
CVSS v3.1 score: 8.2