CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-113714

CVE-2026-113714: Cache Poisoning and Unvalidated Wheel Download in virtualenv

Amit Schendel
Amit Schendel
Senior Security Researcher

Oct 24, 2026·5 min read·5 visits

Executive Summary (TL;DR)

virtualenv versions prior to 21.14.9 suffer from cache poisoning due to unvalidated wheel writes into shared storage and stdout parsing vulnerabilities during package downloads.

Prior to version 21.14.9, virtualenv downloads wheel files directly into its reusable persistent cache directory prior to verifying PyPI digests or compatibility. Combined with unsanitized stdout parsing from pip, a controlled package index can cause virtualenv to calculate digests against pre-existing local files while leaving unverified archives in the shared persistent cache.

Vulnerability Overview

The virtualenv package is a Python utility maintained by the Python Packaging Authority (PyPA) for generating isolated Python execution environments. During environment creation, virtualenv retrieves support dependencies such as pip, setuptools, and wheel. Downloaded binaries are cached in a shared, persistent local directory at wheel/house to accelerate subsequent environment creations.

In releases prior to version 21.14.9, virtualenv writes package downloads directly into this shared persistent cache before performing cryptographic digest validation or environment compatibility checks. Furthermore, virtualenv determines which file was downloaded by parsing text returned on standard output by pip during execution.

This architectural sequence creates a vulnerability to local cache poisoning when virtualenv interacts with a controlled or compromised package index. An attacker can manipulate output lines to bypass hash checks and commit unverified package archives into persistent storage.

Root Cause Analysis

The core flaw exists in virtualenv/seed/wheels/acquire.py inside the download_wheel() function. When fetching seed packages, virtualenv executes pip download passing the permanent wheel/house folder directly via the -d argument. Network responses are written straight into persistent storage before any cryptographic verification occurs.

To identify the target file for hash verification, _find_downloaded_wheel() parses pip stdout line by line, searching for prefixes such as Saved or File was already downloaded . When rendering log output for HTML index pages, pip echoes certain HTML tag attributes, including data-requires-python. A custom or malicious package index serving HTML responses can embed arbitrary text within these attributes.

When pip download processes an HTML link containing an attribute formatted like data-requires-python="Saved /path/to/genuine/wheel.whl", pip writes that text to standard output. virtualenv parses this line and targets the local, genuine file for SHA-256 digest validation. Consequently, verification succeeds against the local file while the unverified archive downloaded from the package index remains stored in wheel/house. In addition, when digest verification fails normally, virtualenv raises an exception but leaves the unverified binary on disk.

Code Analysis & Patch Inspection

In vulnerable versions prior to 21.14.9, download_wheel() invokes pip download with direct destination output pointing to the permanent cache:

# Vulnerable implementation in acquire.py:
cmd = [
    sys.executable, "-c", PIP_RUN, "download",
    "--progress-bar", "off",
    "--only-binary=:all:",
    "--no-deps",
    "--python-version", for_py_version,
    "-d", str(to_folder), # Direct write into permanent cache
    to_download,
]

The fix introduced in Pull Request #3430 and commit 7babec66e043e1cc8bc66869a063653898719d96 refactors the workflow to download packages into an isolated temporary directory created with tempfile.TemporaryDirectory(dir=to_folder, prefix="download-"). The stdout parsing logic was replaced with direct filesystem inspection using folder.iterdir(), enforcing strict file count and specifier rules.

The patched implementation performs verification while the binary resides in staging:

# Fixed implementation in virtualenv 21.14.9:
with TemporaryDirectory(dir=to_folder, prefix="download-") as staging:
    cmd: Final = [
        sys.executable, "-c", PIP_RUN, "download",
        "--progress-bar", "off", "--disable-pip-version-check",
        "--only-binary=:all:", "--no-deps",
        "--python-version", for_py_version,
        "-d", staging, # Isolated temporary staging directory
        to_download,
    ]
    process: Final = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE, universal_newlines=True, encoding="utf-8")
    out, err = process.communicate()
    if process.returncode != 0:
        raise CalledProcessError(process.returncode, cmd, output=out, stderr=err)
    
    # Inspect downloaded file directly from directory
    result: Final = _downloaded_wheel(Path(staging), distribution, version_spec)
    
    # Verify digest and Python compatibility in isolation
    if verify_against_pypi:
        verify_wheel_digest(result)
    if not result.support_py(for_py_version):
        raise RuntimeError(f"downloaded wheel {result.name} does not support Python {for_py_version}")
        
    # Atomically promote verified archive to persistent destination
    destination: Final = to_folder / result.name
    result.path.replace(destination)
    return Wheel(destination)

Additionally, get_pip_install_cmd in virtualenv/seed/embed/pip_invoke.py was updated to supply --find-links pointing to a private temporary directory populated strictly with selected wheels while passing --no-index to block arbitrary index lookups during environment seeding.

Exploitation & Attack Scenarios

Exploiting CVE-2026-113714 requires that a user run virtualenv while configured to query an attacker-controlled or compromised package index. This scenario occurs in enterprise networks using custom package mirrors, internal index servers, or modified PIP_INDEX_URL settings.

In the forged output vector, the malicious index returns HTML containing anchor attributes designed to inject stdout strings, such as data-requires-python="Saved /absolute/path/to/trusted.whl". When pip prints the attribute text to stdout, virtualenv parses the line and calculates the cryptographic digest of the existing trusted file on disk.

Meanwhile, pip download writes the malicious package binary into wheel/house. Because digest verification succeeded against the trusted path, no exception is triggered during download processing. On subsequent executions of virtualenv, the local discovery routine find_compatible_in_house() retrieves the unverified malicious wheel from wheel/house and installs it without re-initiating PyPI digest verification.

Impact & Risk Assessment

The operational impact of this vulnerability is local cache poisoning, tracked under CWE-354 (Improper Validation of Integrity Check Value). Successful exploitation allows an attacker to inject unverified Python binaries into the local virtualenv cache shared across environments on the host.

CVSS v4.0 scores this issue at 2.3 (Low) with vector CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N. Attack complexity is rated High because it requires controlling index HTTP responses and obtaining knowledge of valid target filesystem paths. User interaction is required to initiate virtualenv execution against the custom index.

Despite the Low base CVSS score, the issue presents heightened risk in shared build infrastructure, multi-tenant CI/CD runners, and developer environments utilizing internal package mirrors. A poisoned persistent cache directory affects subsequent build jobs on the same machine, leading to arbitrary code execution inside created virtual environments.

Remediation & Cache Cleanup Guidance

To resolve CVE-2026-113714, upgrade virtualenv to version 21.14.9 or higher. The update replaces standard output parsing with directory inspection and isolates unverified downloads in temporary staging folders prior to atomic promotion.

Organizations running vulnerable versions of virtualenv that interact with custom package indices should clear local cache directories to eliminate unverified artifacts. Executing virtualenv --clear-app-data or deleting ~/.cache/virtualenv/wheel/house removes stored binaries.

For distribution maintainers unable to update to 21.14.9 immediately, backporting commit 7babec66e043e1cc8bc66869a063653898719d96 from PR #3430 mitigates the flaw by enforcing temporary directory staging and atomic replacement.

Official Patches

PyPAPull Request #3430: Fix wheel download verification and cache placement

Fix Analysis (1)

Technical Appendix

CVSS Score
2.3/ 10
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N
EPSS Probability
0.21%
Top 89% most exploited

Affected Systems

virtualenv packages prior to version 21.14.9Python build environments seeding wheels from custom package indices

Affected Versions Detail

Product
Affected Versions
Fixed Version
virtualenv
PyPA
< 21.14.921.14.9
AttributeDetail
Vulnerability IDCVE-2026-113714 / GHSA-494p-63j8-28p3
CWE IDCWE-354 (Improper Validation of Integrity Check Value)
CVSS v4.0 Score2.3 (Low)
Attack VectorNetwork (AV:N/AC:H/AT:P/PR:N/UI:P)
EPSS Score0.00212 (10.91th percentile)
Exploit StatusUnproven / None
CISA KEV StatusNot Listed
Patched Versionvirtualenv 21.14.9

MITRE ATT&CK Mapping

T1195.002Supply Chain Compromise: Compromise Software Dependencies / Repositories
Initial Access
CWE-354
Improper Validation of Integrity Check Value

The software computes an integrity check value for data, but fails to validate or incorrectly validates the value prior to storing, caching, or utilizing the data.

Vulnerability Timeline

Vulnerability identified in virtualenv wheel download verification logic
2026-10-16
Fix developed and committed in Pull Request #3430
2026-10-17
virtualenv version 21.14.9 released
2026-10-20
CVE-2026-113714 and GHSA-494p-63j8-28p3 published
2026-10-20

References & Sources

  • [1]GitHub Security Advisory GHSA-494p-63j8-28p3
  • [2]virtualenv PR #3430
  • [3]virtualenv Commit 7babec66e043e1cc8bc66869a063653898719d96
  • [4]virtualenv 21.14.9 Release Tag
  • [5]NVD Vulnerability Detail - CVE-2026-113714
  • [6]CVE Record on CVE.org

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•CVE-2026-113723
7.1

CVE-2026-113723: OS Command Injection via Unsafe Prompt Expansion in PyPA virtualenv

A command injection vulnerability in PyPA virtualenv prior to version 21.14.9 allows local attackers to execute arbitrary commands when a user sources a generated shell activation script containing a crafted prompt value.

Alon Barad
Alon Barad
1 views•5 min read
•about 2 hours ago•CVE-2026-113720
4.4

CVE-2026-113720: Local Permission Inheritance Vulnerability in virtualenv

virtualenv prior to version 21.14.9 contains a local permission inheritance vulnerability in its app-data copy seeder. Due to the reliance on Python's standard shutil.copy() function, permissions from cached packages are replicated directly onto newly created virtual environment files, ignoring the active process umask and allowing unprivileged local attackers to inject code.

Amit Schendel
Amit Schendel
5 views•5 min read
•about 3 hours ago•CVE-2026-113718
7.1

CVE-2026-113718: Code Injection in pypa/virtualenv XonshActivator via Multibyte Encoding Misalignment

A code injection vulnerability in pypa/virtualenv prior to version 21.14.9 allows arbitrary command execution in xonsh shells using legacy multibyte encodings when sourcing activate.xsh.

Amit Schendel
Amit Schendel
6 views•5 min read
•about 5 hours ago•CVE-2026-113717
2.3

CVE-2026-113717: Package Digest Verification Bypass in virtualenv Pip Seeder

A verification bypass vulnerability in virtualenv allows unverified Python wheel installation when using pip seeding with the download flag. Affected versions fail to perform SHA-256 digest validation against PyPI metadata before invoking pip.

Alon Barad
Alon Barad
6 views•5 min read
•about 6 hours ago•CVE-2026-113713
4.4

CVE-2026-113713: Insecure Temporary File Symlink Handling in PyPA virtualenv

Insecure temporary file usage (CWE-377) in virtualenv app-data seeder creates dangling symlinks pointing to deleted temporary directories, allowing local attackers on shared systems to execute arbitrary code.

Amit Schendel
Amit Schendel
5 views•4 min read
•about 7 hours ago•CVE-2026-112937
6.1

CVE-2026-112937: Arbitrary Directory Deletion via TOCTOU Race Condition in filelock ReadWriteLock

A Time-of-Check Time-of-Use (TOCTOU) race condition in the Python `filelock` library (versions 4.1.0 to 4.2.0) allows local unprivileged attackers to cause arbitrary directory deletion by swapping file system targets during lock cleanup or dead owner sweeps.

Alon Barad
Alon Barad
7 views•5 min read