Oct 24, 2026·4 min read·4 visits
virtualenv versions 21.14.8 and 21.14.9 leave dangling symlinks when transient application data directories are deleted, enabling local code execution on shared systems.
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.
The Python package virtualenv is a foundational component of the Python software ecosystem used to create isolated execution environments. To populate new environments with core utility packages—including pip, setuptools, and wheel—virtualenv utilizes seeder mechanisms, primarily the app-data seeder (FromAppData).
When the default application data directory is unwritable or restricted by system permissions, virtualenv falls back to creating a transient storage directory within system temporary storage (such as /tmp). When configured to seed packages via symbolic links, virtualenv creates symlinks in the new environment's site-packages directory that point directly into this transient application data cache.
Upon completion of the virtual environment creation process, virtualenv performs cleanup operations and deletes the transient application data folder. This leaves dangling symbolic links inside the target environment's site-packages directory pointing to file paths that no longer exist.
The root cause of CVE-2026-113713 lies in incomplete validation of the application data directory's transient state during option evaluation within the FromAppData seeder class, categorized under CWE-377 (Insecure Temporary File).
While command-line option parsing included logic to explicitly disable --symlink-app-data whenever temporary application data was active, this protection was bypassed when symlink seeding was requested through environment variables (such as VIRTUALENV_SYMLINK_APP_DATA=true) or configuration file settings in virtualenv.ini.
The constructor for FromAppData assigned self.symlinks = options.symlink_app_data without verifying whether self.app_data.transient evaluated to True. Consequently, symlinks were generated pointing into transient directory structures, which were subsequently deleted from the host filesystem upon completion of environment creation.
Analyzing the implementation within src/virtualenv/seed/embed/via_app_data/via_app_data.py highlights how configuration option processing was updated in commit b81995c3feab9e1edb453c75ffc6616e1a77335e.
In vulnerable versions 21.14.8 and 21.14.9, the initialization method directly trusted options.symlink_app_data without evaluating self.app_data.transient. The patch updates the assignment to enforce an explicit conditional check that suppresses symlink creation if the application data location is transient.
class FromAppData(BaseEmbed):
def __init__(self, options: VirtualEnvOptions) -> None:
super().__init__(options)
# Vulnerable:
# self.symlinks = options.symlink_app_data
# Patched:
self.symlinks = options.symlink_app_data and not self.app_data.transient
if options.symlink_app_data and self.app_data.transient:
LOGGER.debug("copy seed packages because temporary app-data is removed after creation")By ensuring that self.symlinks resolves to False whenever self.app_data.transient is True, virtualenv safely falls back to copying seed package files instead of creating links to transient paths.
Exploitation occurs in shared multi-user operating system environments where unprivileged users share access to common temporary directories such as /tmp.
An attacker monitors system temporary directories or predicts the temporary directory path pattern created during virtualenv execution. When a victim user creates a virtual environment using transient application data while symlinking is configured via environment variables, virtualenv creates dangling links and deletes the temporary directory.
The attacker subsequently creates the expected directory structure in the shared temporary directory and inserts a malicious Python module (e.g., pip/__init__.py). When the victim invokes Python binaries or imports modules from within the affected virtual environment, the Python interpreter resolves the dangling symlink to the attacker-controlled path, executing the malicious code under the victim user context.
Successful exploitation allows an unprivileged local attacker to execute arbitrary code with the full privileges of the victim user. In shared workstation or multi-tenant CI/CD build environments, this enables local privilege escalation to match the targeted user account.
The vulnerability is assessed with a CVSS v4.0 Base Score of 4.4 (CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N). The High Integrity rating reflects the complete loss of control over code executed within the victim's Python virtual environment.
The EPSS score is 0.00107, placing it in the 1.10th percentile for predicted exploitation probability. The flaw is not listed in the CISA KEV catalog, and no public weaponized exploit code has been identified.
To remediate CVE-2026-113713, upgrade virtualenv to version 21.14.10 or later using standard Python package management tools:
pip install --upgrade virtualenv>=21.14.10If immediate upgrading is not feasible, administrators should audit multi-user build environments to ensure VIRTUALENV_SYMLINK_APP_DATA is not enabled in environment variables or system-wide virtualenv.ini configuration files.
System administrators should also enforce directory access controls on shared temporary storage, ensuring the sticky bit (chmod +t /tmp) is enabled and utilizing isolated temporary directory namespaces (PrivateTmp=true in systemd services) for automated build workflows.
CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:P/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
virtualenv PyPA | >= 21.14.8, < 21.14.10 | 21.14.10 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-113713 |
| Vulnerability Type | Insecure Temporary File (CWE-377) |
| CVSS v4.0 Score | 4.4 (Medium) |
| EPSS Score | 0.00107 (1.10th percentile) |
| Affected Component | virtualenv app-data seeder (FromAppData) |
| Impact | Local code execution via dangling symlink hijacking |
| CISA KEV Status | Not Listed |
| Fixed Version | 21.14.10 |
The software creates or uses a temporary file in an insecure manner, which can allow an attacker to read, modify, or replace file contents.
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.
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.
A local race condition and mutual exclusion bypass vulnerability exists in filelock versions 4.1.0 through 4.2.0. On Linux filesystems with rapid inode reuse, a local attacker can manipulate modification timestamps and lock files to cause waiting processes to falsely evict active soft lock markers.
vLLM prior to version 0.31.0 contains an uncontrolled resource consumption vulnerability (CWE-400) in its HTTP API request handling logic. Unauthenticated remote attackers can transmit per-request keyword arguments (`mm_processor_kwargs` and `media_io_kwargs`) that are passed directly into internal multimodal preprocessing pipelines, causing CPU/GPU memory exhaustion and crashing the engine process.
An insufficient verification of data authenticity vulnerability in SiYuan prior to version 3.7.4 allows an attacker to overwrite arbitrary installed marketplace packages (plugins, widgets, themes, and templates). By providing a mismatched package name and package archive repository URL during a Bazaar installation request, arbitrary file write into trusted plugin directories can be achieved, enabling persistence and execution across application restarts.
In obsidian-tc versions 1.0.1 through 1.31.3, memory lifecycle tools fail to validate note ownership prior to rematerializing Markdown files and execute non-atomic SQLite database mutations. An authorized caller can overwrite arbitrary note bodies on disk and cause persistent desynchronization between the database memory graph and physical vault files.