Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessGain local account, plant symlink in read-only dir
-
ExploitVictim runs virtualenv, entry deletion fails
-
Executiononerror handler chmods symlink target writable
-
ImpactCo-resident account writes to victim-owned file
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V5 - File Handling Symlink resolution safety
|
| Exploitation Conditions | Requires a local user on a POSIX system who can plant a symlink inside a read-only directory that virtualenv's safe_delete() will traverse; the deletion of that entry must fail so the onerror handler runs and calls os.chmod() on the symlink target, following the link. The symlink target must be owned by the invoking (victim) user and mode-changeable by them, and must start out not writable. For a different account to actually abuse the widened mode, that account must be able to traverse the target's parent directories. Limiting factors: local access only, a specific read-only-directory/symlink filesystem layout, a genuine deletion failure to trigger the handler, the victim actually running virtualenv, and the fact that root does not reach the demonstrated failure path - all of which sharply narrow realistic exploitation. |
| Impact | Signals converge on LOW real-world priority despite the symlink-following (CWE-59) root cause. The provided CVSS 3.1 vector (AV:L/AC:H/PR:L/UI:N) describes a local, high-complexity, authenticated-only issue with only partial (Low) impact, yielding a base score of 4.5 (Medium-low). No EPSS score, KEV listing, or public POC is present in the supplied intelligence, so exploitation status is 'no public exploit identified at time of analysis' and there is no SSVC decision data to weigh. The attack requires an unusual filesystem setup (a planted symlink inside a read-only directory, a deletion that fails and triggers the retry handler, and a target owned by and mode-changeable by the invoking user), and the advisory itself notes root does not reach the failure path and the operation never reads the target - all of which strongly bound exploitability. Conflicting signal to flag: the CVSS vector and the 'Information Disclosure' tag imply confidentiality impact, but the description explicitly states the operation does not read the target and only widens write permissions, so the labeled impact is integrity/permission tampering, not disclosure - verify impact classification against the vendor advisory. Net: a legitimate hardening fix, not an urgent, widely-exploitable threat. |
| Exploit ScenarioDerived | On a shared POSIX host, a local user plants a symlink inside a read-only directory that virtualenv will attempt to clean up; when safe_delete() fails to remove the entry, its onerror handler calls os.chmod() on the symlink's target (following the link) and adds owner-write, leaving a file the invoking user owns but did not intend to expose in a writable state. A co-resident account that can traverse the target's parent directories then tampers with that file. No public exploit code is identified, and the high attack complexity (AC:H) plus required local, low-privilege access make opportunistic exploitation unlikely. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Oct 20, 2026
Symlink following in virtualenv's safe_delete() deletion-error handler (all versions prior to 21.14.9) lets a local attacker widen the permissions of a victim-owned file located outside the environment being created or removed. When removal of an entry inside a read-only directory fails, the handler reads mode bits with os.lstat() and then calls os.chmod() on the same path, which resolves symlinks - so a planted link causes S_IWUSR to be added to an out-of-scope target the victim owns and can chmod, provided that target starts out non-writable. Exploitation requires local access, a specific read-only-directory/symlink filesystem layout, a genuine deletion failure and the victim actually running virtualenv, so practical risk is low; no public exploit code has been identified at time of analysis and the issue is not in CISA KEV.
RemediationAI
Upgrade to virtualenv 21.14.9 or later (Vendor-released patch: 21.14.9), available from the release page at https://github.com/pypa/virtualenv/releases/tag/21.14.9 and the vendor advisory https://github.com/pypa/virtualenv/security/advisories/GHSA-43mp-35q3-x3fq; where the package is pinned by an OS distribution, verify the backport or pin the fixed version explicitly. Until patched, run virtualenv only from directories whose contents and parent chain are controlled by the invoking user or root - a planted link inside a world- or group-writable read-only directory is the delivery vector - and audit existing environments for unexpected symlinks before rebuilds. Note the fix's behavior change: on POSIX, read-only deletion failures are now surfaced as errors instead of being silently retried after a chmod, so automation that relied on the old best-effort cleanup may need to pre-clear permissions or handle the raised exception; on Windows, attribute clearing now refuses reparse points, so junctions/symlinks inside an environment will cause an explicit error rather than being followed.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in pypi
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
virtualenv's cleanup path (src/virtualenv/util/path/_sync.py) uses shutil.rmtree with an onerror callback named safe_delete. On a failed os.unlink/os.rmdir, the pre-21.14.9 handler checked os.access(path, os.W_OK) and then did os.chmod(path, os.lstat(path).st_mode | S_IWUSR). The mismatch is the classic CWE-59 (link following) pattern: os.lstat() deliberately does not dereference links, but os.chmod() always does, so for a symbolic link the write bit is applied to the link's destination rather than to the link itself. On POSIX, deletion of a directory entry depends on the writability of the parent directory, not of the entry, so the chmod retry could never make the failing unlink succeed - it only produced the side effect on the link target. The fix in 21.14.9 (PR 3427, commit 2bfdc02) makes the POSIX branch propagate the original exception instead of chmod-ing, and confines permission handling to Windows, where a new clear_readonly() helper opens the entry itself with CreateFileW and deny-replacement sharing, reads FILE_BASIC_INFO via GetFileInformationByHandleEx, refuses to touch reparse points, and clears the read-only attribute via SetFileInformationByHandle.
Know when Virtualenv gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L
Requires a local account, a planted symlink, and a deletion failure (AV:L/AC:H/PR:L); a separate victim must run the tool (UI:R); it only widens write access and never reads the target (I:L, C:N).
Primary rating from Vendor (GitHub_M).
CVSS VectorVendor: GitHub_M
Lifecycle Timeline
2DescriptionCVE.org
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, the safe_delete() deletion error handler responds to a failed removal by reading mode bits with os.lstat(path) and applying owner-write permission with os.chmod(path, ...), which follows a symlink. A planted link inside a read-only directory can therefore cause a victim-owned target outside the destination that is initially not writable by the invoking user to become broadly writable before the retry fails, provided that user owns the target and can change its mode. Another account can use the widened mode only if it can traverse the target's parent directories; root does not reach the demonstrated failure path, and the operation does not itself read the target. This issue is fixed in version 21.14.9.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.
More in Virtualenv
View allvirtualenv before 20.26.6 allows command injection through the activation scripts for a virtual environment. Rated high
virtualenv versions prior to 21.7.12 cache and seed pip/setuptools wheels without verifying their bytes, so an attacker
Configuration injection in PyPA virtualenv before 21.7.11 lets a line boundary embedded in a prompt value inject extra k
Command injection in PyPA virtualenv before 21.14.9 lets an attacker who controls the destination path of a virtual envi
Command injection in virtualenv's bash activation script prior to 21.14.7 allows a crafted environment name or --prompt
Command injection in the bash, zsh, and fish activation scripts generated by virtualenv releases prior to 21.7.13 lets a
Command injection in PyPA virtualenv before 21.7.12 allows an attacker who controls the environment's prompt value - via
Command injection in virtualenv's bash activator lets an attacker-supplied --prompt value be stored unescaped in PS1 and
Caller-influenced prompt text in virtualenv's bash activation script prior to 21.14.9 can be re-expanded by bash as exec
A code injection flaw in PyPA virtualenv before 21.14.9 lets attacker-influenced non-ASCII characters in the xonsh promp
Symlink-following file writes in virtualenv before 21.14.9 let a second, unprivileged local account redirect generated e
Path traversal in PyPA virtualenv's seed-package cleanup lets a local, low-privileged attacker delete files outside the
EUVD-2026-105272
GHSA-43mp-35q3-x3fq
NVD
CVE.org
EPSS
Exploit-DB