Lack of data validation - Path Traversal In pyload-ng
Description
pyload-ng: Incomplete Tar Path Traversal Fix in UnTar._safe_extractall via os.path.commonprefix Bypass
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 ...
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...
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 ...
This removes the broken inline function and uses the already-existing correct implementation that was added in the GHSA-7g4m-8hx2-4qh3 fix.
Mitigation
Update Impact
Minimal update. May introduce new vulnerabilities or breaking changes.
Ecosystem | Package | Affected version | Patched versions |
|---|---|---|---|
pypi | 0.5.0b3.dev97 |
Aliases
References