CVE-2026-113722
virtualenv: Generated POSIX console launchers interpret destination paths as shell syntax
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.8
virtualenv: Generated POSIX console launchers interpret destination paths as shell syntax
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
CVE-2026-113722- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-78
- Updated
- 2026-11-02
EUVDEUVD-2026-105304
7.8
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
EUVD-2026-105304- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-c978-59m8-c77v
7.8
virtualenv: Generated POSIX console launchers interpret destination paths as shell syntax
Summary
A destination path supplied to virtualenv can be interpreted as shell syntax by a generated POSIX console launcher. The app-data seeder and pip delegate launcher generation to distlib but supply the interpreter path in a form that does not preserve it as literal data in the shell wrapper.
Affected area
POSIX console scripts generated by the app-data seeder or pip when the interpreter path requires a shell wrapper. Windows executable launchers are not affected by this path.
Attacker-controlled input
An attacker must influence the environment destination passed to virtualenv, and the user must subsequently run a generated launcher. The attacker does not need to modify an installed executable or control the user's app-data cache.
Affected code
The relevant 21.14.8 source is in ScriptMakerCustom.
Mechanism
The app-data seeder passes the environment's interpreter path through enquote_executable() to distlib's ScriptMaker.
It adds double quotes for a path with a space but does not escape shell expansions inside those quotes. When the path
has a space or the shebang exceeds 127 bytes, distlib emits a #!/bin/sh wrapper with a line shaped like
'''exec' "<path>" "$0" "$@". The shell expands $(...) and backticks in that path when the launcher is run. The same
wrapper must also parse as Python, which exposes additional quote and backslash cases. Environment creation and
activation do not trigger the shell payload.
Reproduction and evidence
The scanner created three environments: a control under my -touch PWNED- project, one under
my $(touch PWNED) project, and one under a path of more than 127 bytes ending in $(touch${IFS}PWNED). Each was
created without producing PWNED. Running the generated bin/pip --version created PWNED in both attack cases; the
control created none. The long-path case reached the shell wrapper without a space. This reproduces execution by the
generated launcher, with the user's privileges, rather than during seeding.
The report dates the app-data seeder's distlib launcher integration to #1579, first released in 20.0.2. PR #3417 quotes the raw interpreter path for the POSIX wrapper and repairs launchers written by the pip seeder. Pip's own distlib can write a vulnerable wrapper again during a later pip install or upgrade; the proposed virtualenv fix does not change pip's vendored code. The maintainer-provided follow-up review reports that later path separately.
Attack sequence
sequenceDiagram
participant A as Destination supplier
participant V as virtualenv seeder
participant D as distlib ScriptMaker
participant S as Generated bin/pip
participant U as User
A->>V: path contains $(touch PWNED)
V->>D: pass interpreter path with incomplete shell quoting
D->>S: write /bin/sh wrapper
Note over V,S: no payload runs during creation or activation
U->>S: pip --version
S->>S: shell expands path and runs touch
S-->>U: PWNED appears with user's privileges
Reproducer files
The following files are from the scanner report for ANT-2026-5DZVLGL1. 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 shutil
import subprocess
from pathlib import Path
WORK = Path("/tmp/repro-console-script")
# The attacker's whole input: the name of a directory above the environment (a repository, a project folder).
DIRECTORY_NAMES = {
"CONTROL": "my -touch PWNED- project",
…
- Record
GHSA-c978-59m8-c77v- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-78
- Updated
- 2026-11-02
NVD
7.8
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
CVE-2026-113722- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-78
- Updated
- 2026-11-02
OSV
7.8
virtualenv: Generated POSIX console launchers interpret destination paths as shell syntax
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
CVE-2026-113722- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-78
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-113722
7.8
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
DEBIAN-CVE-2026-113722- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
Show 7 moreShow less
OSVGHSA-c978-59m8-c77v
7.8
virtualenv: Generated POSIX console launchers interpret destination paths as shell syntax
Summary
A destination path supplied to virtualenv can be interpreted as shell syntax by a generated POSIX console launcher. The app-data seeder and pip delegate launcher generation to distlib but supply the interpreter path in a form that does not preserve it as literal data in the shell wrapper.
Affected area
POSIX console scripts generated by the app-data seeder or pip when the interpreter path requires a shell wrapper. Windows executable launchers are not affected by this path.
Attacker-controlled input
An attacker must influence the environment destination passed to virtualenv, and the user must subsequently run a generated launcher. The attacker does not need to modify an installed executable or control the user's app-data cache.
Affected code
The relevant 21.14.8 source is in ScriptMakerCustom.
Mechanism
The app-data seeder passes the environment's interpreter path through enquote_executable() to distlib's ScriptMaker.
It adds double quotes for a path with a space but does not escape shell expansions inside those quotes. When the path
has a space or the shebang exceeds 127 bytes, distlib emits a #!/bin/sh wrapper with a line shaped like
'''exec' "<path>" "$0" "$@". The shell expands $(...) and backticks in that path when the launcher is run. The same
wrapper must also parse as Python, which exposes additional quote and backslash cases. Environment creation and
activation do not trigger the shell payload.
Reproduction and evidence
The scanner created three environments: a control under my -touch PWNED- project, one under
my $(touch PWNED) project, and one under a path of more than 127 bytes ending in $(touch${IFS}PWNED). Each was
created without producing PWNED. Running the generated bin/pip --version created PWNED in both attack cases; the
control created none. The long-path case reached the shell wrapper without a space. This reproduces execution by the
generated launcher, with the user's privileges, rather than during seeding.
The report dates the app-data seeder's distlib launcher integration to #1579, first released in 20.0.2. PR #3417 quotes the raw interpreter path for the POSIX wrapper and repairs launchers written by the pip seeder. Pip's own distlib can write a vulnerable wrapper again during a later pip install or upgrade; the proposed virtualenv fix does not change pip's vendored code. The maintainer-provided follow-up review reports that later path separately.
Attack sequence
sequenceDiagram
participant A as Destination supplier
participant V as virtualenv seeder
participant D as distlib ScriptMaker
participant S as Generated bin/pip
participant U as User
A->>V: path contains $(touch PWNED)
V->>D: pass interpreter path with incomplete shell quoting
D->>S: write /bin/sh wrapper
Note over V,S: no payload runs during creation or activation
U->>S: pip --version
S->>S: shell expands path and runs touch
S-->>U: PWNED appears with user's privileges
Reproducer files
The following files are from the scanner report for ANT-2026-5DZVLGL1. 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 shutil
import subprocess
from pathlib import Path
WORK = Path("/tmp/repro-console-script")
# The attacker's whole input: the name of a directory above the environment (a repository, a project folder).
DIRECTORY_NAMES = {
"CONTROL": "my -touch PWNED- project",
…
- Record
GHSA-c978-59m8-c77v- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-78
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-113722
7.8
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
UBUNTU-CVE-2026-113722- CVSS 3.1
- 7.8 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
OSVAZL-110112 No score
CVE-2026-113722 affecting package python-virtualenv for versions less than 20.36.1-10
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
AZL-110112- Updated
- 2026-11-07
OSVBIT-virtualenv-2026-113722 No score
virtualenv: Generated POSIX console launchers interpret destination paths as shell syntax
virtualenv is a tool for creating isolated virtual python environments. Prior to 21.14.9, virtualenv's ScriptMakerCustom and pip pass an incompletely quoted interpreter path to distlib ScriptMaker when generating POSIX console scripts. When the path contains a space or produces a shebang longer than 127 bytes, distlib emits a /bin/sh wrapper whose double-quoted interpreter path still expands command substitutions and backticks. An attacker who influences the environment destination can therefore execute commands with the user's privileges when the user later runs a generated launcher; environment creation and activation do not trigger the command. Existing launchers are not repaired by upgrading virtualenv alone and must be regenerated, while a later independent pip installation or upgrade can reintroduce an affected launcher because pip's vendored distlib is unchanged. The virtualenv fix is released in version 21.14.9.
- Record
BIT-virtualenv-2026-113722- Updated
- 2026-11-02
OSVCLSA-2026-1793107254 No score
TuxCare security update for virtualenv (8 CVEs)
TuxCare rebuilt virtualenv to address 8 CVEs. Fixed in 20.39.1.post5+tuxcare.
- Record
CLSA-2026-1793107254- 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
OSVROOT-APP-PYPI-CVE-2026-113722 No score
CVE-2026-113722 in virtualenv - Patched by Root
Root has patched CVE-2026-113722 in the virtualenv package for Root:PyPI. Multiple fixed versions available.
- Record
ROOT-APP-PYPI-CVE-2026-113722- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
virtualenvPyPI |
before 21.14.9 | 21.14.9 |
python-virtualenvAzure Linux:3 |
before 20.36.1-10 | 20.36.1-10 |
virtualenvBitnami |
before 21.14.9 | 21.14.9 |
virtualenvTuxCare:PyPI From an advisory covering 8 CVEs |
20.39.1 up to 20.39.1.post5+tuxcare, plus listed versions 20.39.1.post1+tuxcare20.39.1.post2+tuxcare20.39.1.post3+tuxcare20.39.1.post4+tuxcare |
20.39.1.post5+tuxcare |
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 |
virtualenvRoot:PyPI |
before 20.26.6+aikido.5 | 20.26.6+aikido.521.14.5+aikido.221.3.2+aikido.321.7.1+aikido.321.4.3+aikido.221.13.0+aikido.121.7.5+aikido.421.14.1+aikido.121.7.9+aikido.321.9.0+aikido.121.11.1+aikido.121.7.10+aikido.421.12.1+aikido.121.7.8+aikido.321.14.6+aikido.221.9.0+aikido.221.7.13+aikido.221.11.1+aikido.221.7.10+aikido.521.7.4+aikido.621.14.2+aikido.221.6.1+aikido.321.4.2+aikido.221.12.1+aikido.221.9.0+aikido.321.7.5+aikido.521.7.1+aikido.421.12.1+aikido.321.7.9+aikido.421.4.3+aikido.321.2.4+aikido.421.7.16+aikido.221.3.2+aikido.4 |
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 |
Show 8 moreShow less
| Package | Affected | Fixed |
|---|---|---|
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 |
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 | OSV | |
| 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-113722 is a high vulnerability (CVSS 7.8) 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.29%, the 21st percentile. Fixed in virtualenv 21.14.9.
References
github.com/pypa/virtualenv/security/advisories/GHSA-c978-59m8-c77v
github.com/pypa/virtualenv/pull/3417
github.com/pypa/virtualenv/commit/2519941bed33ee0d3ac99277f67913e439e476a9
github.com/pypa/virtualenv/releases/tag/21.14.9
nvd.nist.gov/vuln/detail/CVE-2026-113722
github.com/advisories/GHSA-c978-59m8-c77v
Show 14 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/113xxx/CVE-2026-113722.json
security-tracker.debian.org/tracker/CVE-2026-113722
github.com/pypa/virtualenv
ubuntu.com/security/CVE-2026-113722
cve.org/CVERecord?id=CVE-2026-113722
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.