CVE-2026-113724
virtualenv: Deletion recovery can change a symlink target's mode
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.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- High
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- Low
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
4.5
virtualenv: Deletion recovery can change a symlink target's mode
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.
- Record
CVE-2026-113724- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
EUVDEUVD-2026-105272
4.5
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.
- Record
EUVD-2026-105272- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-43mp-35q3-x3fq
4.5
virtualenv: Deletion recovery can change a symlink target's mode
Summary
The deletion error handler obtains permission bits with lstat() but applies them through chmod(), which follows symlinks. On an eligible unlink failure this can change permissions outside the destination.
Anthropic's OSS Scanner reported this as ANT-2026-UK90QC27 in virtualenv-20261015-005, received on 15 October 2026.
Affected area
Permission recovery after a filesystem deletion error.
Attacker-controlled input
A deletion must fail on a symbolic-link entry and enter the permission-recovery branch. The invoking user must have permission to change the target mode. This is conditional error handling, not the normal successful unlink path.
Affected code
The relevant 21.14.8 source is in safe_delete().
Mechanism
safe_delete() asks shutil.rmtree() to retry a failed removal after adding owner-write permission. Its error handler
calls os.lstat(path) for mode bits but os.chmod(path, ...) to apply them. For a symlink, lstat reads the link's
mode while chmod follows the link. A link inside a read-only directory can therefore cause the target's mode to become
0777 before the retry fails. The target must be owned by the invoking user and initially not writable by that user;
root does not reach this failure path.
Reproduction and evidence
The scanner packed a project containing a mode-0555 venv/bin/python/ directory and a symlink inside it pointing to
the user's mode-0400 keys/deploy.pem. An unprivileged user unpacked it and ran both plain virtualenv venv and
virtualenv --clear venv. Both commands exited with PermissionError naming the directory inside venv, while the
key's mode changed from 0400 to 0777. A control archive with a writable directory completed normally and left the
key at 0400. The read-only mode survives tar or cp -a; a normal git clone does not preserve this setup.
This changes permissions; it does not read the target or complete the virtualenv operation. Another account can use the widened mode only if it can traverse the target's parent directories. The scanner dates the active error handler to #3222, first released in 21.7.5. PR #3427 avoids POSIX mode changes on deletion failure and handles Windows read-only entries without following reparse points.
Attack sequence
sequenceDiagram
participant A as Prepared read-only venv tree
participant V as safe_delete error handler
participant L as Symlink in venv
participant K as User's 0400 key
A->>V: virtualenv venv or --clear venv
V->>L: unlink fails in 0555 directory
V->>L: lstat returns link mode 0777
V->>L: chmod follows symlink
L->>K: target mode becomes 0777
V-->>A: retry fails with PermissionError
Note over K: Other accounts can use new mode if parent paths are traversable
Reproducer files
The following files are from the scanner report for ANT-2026-UK90QC27. They were tested against commit a857a7823c73
in the scanner image. The repro entrypoint expects companion files under /tmp/repro.d; cases that change user IDs
need a disposable root-capable container. The last output line is BUG when the reported behavior occurs. These files
are included as evidence and were not rerun for this advisory update.
repro
#!/usr/bin/env python3
import os
import stat
import subprocess
import sys
import tempfile
from pathlib import Path
NOBODY = 65534
work = Path(tempfile.mkdtemp(prefix="repro-safe-delete-"))
os.chmod(work, 0o755)
os.chown(work, NOBODY, NOBODY)
# the attacker's project archive (venv/bin/python read-only), and the same archive with that folder writable
make_archive = [sys.executable, "/tmp/repro.d/make_project_archive.py"]
subprocess.run([*make_archive, work / "read-only.tar", "555"], check=True)
subprocess.run([*make_archive, work /…
- Record
GHSA-43mp-35q3-x3fq- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
NVD
4.5
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.
- Record
CVE-2026-113724- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
OSV
4.5
virtualenv: Deletion recovery can change a symlink target's mode
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.
- Record
CVE-2026-113724- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-113724
4.5
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.
- Record
DEBIAN-CVE-2026-113724- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-43mp-35q3-x3fq
4.5
virtualenv: Deletion recovery can change a symlink target's mode
Summary
The deletion error handler obtains permission bits with lstat() but applies them through chmod(), which follows symlinks. On an eligible unlink failure this can change permissions outside the destination.
Anthropic's OSS Scanner reported this as ANT-2026-UK90QC27 in virtualenv-20261015-005, received on 15 October 2026.
Affected area
Permission recovery after a filesystem deletion error.
Attacker-controlled input
A deletion must fail on a symbolic-link entry and enter the permission-recovery branch. The invoking user must have permission to change the target mode. This is conditional error handling, not the normal successful unlink path.
Affected code
The relevant 21.14.8 source is in safe_delete().
Mechanism
safe_delete() asks shutil.rmtree() to retry a failed removal after adding owner-write permission. Its error handler
calls os.lstat(path) for mode bits but os.chmod(path, ...) to apply them. For a symlink, lstat reads the link's
mode while chmod follows the link. A link inside a read-only directory can therefore cause the target's mode to become
0777 before the retry fails. The target must be owned by the invoking user and initially not writable by that user;
root does not reach this failure path.
Reproduction and evidence
The scanner packed a project containing a mode-0555 venv/bin/python/ directory and a symlink inside it pointing to
the user's mode-0400 keys/deploy.pem. An unprivileged user unpacked it and ran both plain virtualenv venv and
virtualenv --clear venv. Both commands exited with PermissionError naming the directory inside venv, while the
key's mode changed from 0400 to 0777. A control archive with a writable directory completed normally and left the
key at 0400. The read-only mode survives tar or cp -a; a normal git clone does not preserve this setup.
This changes permissions; it does not read the target or complete the virtualenv operation. Another account can use the widened mode only if it can traverse the target's parent directories. The scanner dates the active error handler to #3222, first released in 21.7.5. PR #3427 avoids POSIX mode changes on deletion failure and handles Windows read-only entries without following reparse points.
Attack sequence
sequenceDiagram
participant A as Prepared read-only venv tree
participant V as safe_delete error handler
participant L as Symlink in venv
participant K as User's 0400 key
A->>V: virtualenv venv or --clear venv
V->>L: unlink fails in 0555 directory
V->>L: lstat returns link mode 0777
V->>L: chmod follows symlink
L->>K: target mode becomes 0777
V-->>A: retry fails with PermissionError
Note over K: Other accounts can use new mode if parent paths are traversable
Reproducer files
The following files are from the scanner report for ANT-2026-UK90QC27. They were tested against commit a857a7823c73
in the scanner image. The repro entrypoint expects companion files under /tmp/repro.d; cases that change user IDs
need a disposable root-capable container. The last output line is BUG when the reported behavior occurs. These files
are included as evidence and were not rerun for this advisory update.
repro
#!/usr/bin/env python3
import os
import stat
import subprocess
import sys
import tempfile
from pathlib import Path
NOBODY = 65534
work = Path(tempfile.mkdtemp(prefix="repro-safe-delete-"))
os.chmod(work, 0o755)
os.chown(work, NOBODY, NOBODY)
# the attacker's project archive (venv/bin/python read-only), and the same archive with that folder writable
make_archive = [sys.executable, "/tmp/repro.d/make_project_archive.py"]
subprocess.run([*make_archive, work / "read-only.tar", "555"], check=True)
subprocess.run([*make_archive, work /…
- Record
GHSA-43mp-35q3-x3fq- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-59
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-113724
4.5
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.
- Record
UBUNTU-CVE-2026-113724- CVSS 3.1
- 4.5 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L - Updated
- 2026-11-02
OSVAZL-111521 No score
CVE-2026-113724 affecting package python-virtualenv 20.36.1-9
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.
- Record
AZL-111521- Updated
- 2026-11-05
OSVBIT-virtualenv-2026-113724 No score
virtualenv: Deletion recovery can change a symlink target's mode
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.
- Record
BIT-virtualenv-2026-113724- Updated
- 2026-11-02
OSVopenSUSE-SU-2026:12372-1 No score
python313-virtualenv-21.15.1-1.1 on GA media
These are all security issues fixed in the python313-virtualenv-21.15.1-1.1 package on the GA media of openSUSE Tumbleweed.
- Record
openSUSE-SU-2026:12372-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
virtualenvPyPI |
before 21.14.9 | 21.14.9 |
python-virtualenvAzure Linux:3 |
through 20.36.1-9 | None known |
virtualenvBitnami |
before 21.14.9 | 21.14.9 |
python-virtualenvDebian:12 |
All versions, plus listed versions 20.17.1+ds-120.19.0+ds-120.21.0+ds-120.23.0+ds-120.23.0+ds-2 Show 28 moreShow less20.24.1+ds-120.24.6+ds-120.24.6+ds-220.25.0+ds-120.25.0+ds-220.25.1+ds-120.26.1+ds-120.26.2+ds-120.26.6+ds-120.27.0+ds-120.28.0+ds-120.29.1+ds-120.29.3+ds-120.30.0+ds-120.30.0+ds-220.30.0+ds-320.31.2+ds-120.33.1+ds-120.34.0+ds-120.35.3+ds-120.35.4+ds-120.36.1+ds-120.38.0+ds-121.2.0+ds-121.2.4+ds-121.3.1+ds-121.4.1+ds-121.5.1+ds-1 |
None known |
python-virtualenvDebian:13 |
All versions, plus listed versions 20.31.2+ds-120.31.2+ds-1+deb13u120.33.1+ds-120.34.0+ds-120.35.3+ds-1 Show 8 moreShow less20.35.4+ds-120.36.1+ds-120.38.0+ds-121.2.0+ds-121.2.4+ds-121.3.1+ds-121.4.1+ds-121.5.1+ds-1 |
None known |
python-virtualenvDebian:14 |
All versions, plus listed versions 20.31.2+ds-120.33.1+ds-120.34.0+ds-120.35.3+ds-120.35.4+ds-1 Show 7 moreShow less20.36.1+ds-120.38.0+ds-121.2.0+ds-121.2.4+ds-121.3.1+ds-121.4.1+ds-121.5.1+ds-1 |
None known |
virtualenvPyPI |
before 21.14.9, plus listed versions 0.80.8.10.8.20.8.30.8.4 Show 45 moreShow less0.90.9.10.9.21.01.11.101.10.11.111.11.11.11.21.11.31.11.41.11.51.11.61.21.31.3.11.3.21.3.31.3.41.41.4.11.4.21.4.31.4.41.4.51.4.61.4.71.4.81.4.91.4rc11.51.5.11.5.21.61.6.11.6.21.6.31.6.41.71.7.11.7.1.11.7.1.21.7.21.8 |
21.14.9 |
python-virtualenvUbuntu:14.04:LTS |
All versions, plus listed versions 1.10.1-11.11-11.11-21.11.4-11.11.4-1ubuntu1 |
None known |
python-virtualenvUbuntu:16.04:LTS |
All versions, plus listed versions 1.11.6+ds-114.0.5+ds-114.0.5+ds-214.0.5+ds-415.0.0+ds-1 Show 6 moreShow less15.0.0+ds-215.0.1+ds-115.0.1+ds-215.0.1+ds-315.0.1+ds-3ubuntu115.0.1+ds-3ubuntu1.1 |
None known |
python-virtualenvUbuntu:18.04:LTS |
All versions, plus listed versions 15.1.0+ds-115.1.0+ds-1.1 |
None known |
Show 6 moreShow less
| Package | Affected | Fixed |
|---|---|---|
python-virtualenvUbuntu:Pro:20.04:LTS |
All versions, plus listed versions 15.1.0+ds-215.1.0+ds-2build115.1.0+ds-2ubuntu120.0.13-120.0.17-120.0.17-1ubuntu0.320.0.17-1ubuntu0.420.0.17-1ubuntu0.4+esm1 |
None known |
python-virtualenvUbuntu:Pro:22.04:LTS |
All versions, plus listed versions 20.4.6+ds-220.10.0+ds-120.12.1+ds-120.13.0+ds-120.13.0+ds-220.13.0+ds-2ubuntu0.1~esm1 |
None known |
python-virtualenvUbuntu:Pro:24.04:LTS |
All versions, plus listed versions 20.24.1+ds-120.24.6+ds-120.24.6+ds-220.25.0+ds-120.25.0+ds-220.25.0+ds-2ubuntu0.1~esm1 |
None known |
python-virtualenvUbuntu:26.10 |
All versions, plus listed versions 20.36.1+ds-121.4.1+ds-121.5.1+ds-1 |
None known |
python-virtualenvUbuntu:26.04:LTS |
All versions, plus listed versions 20.31.2+ds-120.35.3+ds-120.35.4+ds-120.36.1+ds-1 |
None known |
python-virtualenvopenSUSE:Tumbleweed From an advisory covering 9 CVEs |
before 21.15.1-1.1 | 21.15.1-1.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-113724 is a medium vulnerability (CVSS 4.5) in Pypa Virtualenv, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.20%, the 10th percentile. Fixed in virtualenv 21.14.9.
References
github.com/pypa/virtualenv/security/advisories/GHSA-43mp-35q3-x3fq
github.com/pypa/virtualenv/pull/3427
github.com/pypa/virtualenv/commit/2bfdc0230d1b2685fedfcfe66c835b9a2ec7c440
github.com/pypa/virtualenv/releases/tag/21.14.9
nvd.nist.gov/vuln/detail/CVE-2026-113724
github.com/advisories/GHSA-43mp-35q3-x3fq
Show 14 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/113xxx/CVE-2026-113724.json
security-tracker.debian.org/tracker/CVE-2026-113724
github.com/pypa/virtualenv
ubuntu.com/security/CVE-2026-113724
cve.org/CVERecord?id=CVE-2026-113724
suse.com/security/cve/CVE-2026-113714
suse.com/security/cve/CVE-2026-113716
suse.com/security/cve/CVE-2026-113718
suse.com/security/cve/CVE-2026-113720
suse.com/security/cve/CVE-2026-113722
suse.com/security/cve/CVE-2026-113724
suse.com/security/cve/CVE-2026-113726
suse.com/security/cve/CVE-2026-113732
suse.com/security/cve/CVE-2026-115586
Left out here: 254 versions and the rest of 2 source descriptions. The JSON record has them.