Skip to main content
VulnerabilityPublished

CVE-2026-113723: virtualenv · Bash prompt values can change meaning under older compatibility modes

Affects noxSource GCVE Database

0
Compare
Authoritative record:CVE-2026-113723· High 7.1

Not known to be exploited · CVSS 7.1 High : upgrade to 2026.10.20 or 2022.8.7_1 soon.

Data as of 30 Oct 2026, 06:59 UTC

CVSS 4.0 base score
7.1High
How severe the flaw is on the 0–10 CVSS scale.

Thin evidence

One scorer or none
CVSS scores by source
CNA (GitHub_M)CVSS 4.07.1
EPSS
0.2% chance of exploitation in 30 days – higher than 15% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
Patch
Available – fixed in 2026.10.20, 2022.8.7_1
CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 20 Oct 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponized: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 29 Oct 2026.

What to do

Upgrade nox to 2026.10.20 or 2022.8.7_1.

Affected software

ProductAffectedFixed in
Homebrewnoxpkg:brew/noxOther threatsAffected
  • >=2020.12.31 <2022.8.7_1
  • >=2023.4.22_1 <2026.10.20
Fixed in
  • 2022.8.7_1
  • 2026.10.20

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

## 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", "--nopr

References

Community reviews

Loading reviews…