# virtualenv activation scripts run code from a path that contains a placeholder name

virtualenv's per-key template substitution rewrites placeholder names inside already-quoted values, letting a crafted env path inject shell commands into activate scripts.

- Payout: Research
- Severity: high
- Program: virtualenv
- Found by: Kwstubbs
- Disclosed: 2026-10-21
- CVE: CVE-2026-113776
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-8rjx-v5ww-45pp
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-8rjx-v5ww-45pp
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:python technique:source-review vuln:command-injection

**Target:** virtualenv activation script templates (activate, activate.fish, activate.xsh, activate_this.py, activate.csh)
**Root cause:** ViaTemplateActivator.instantiate_template substitutes placeholders one key at a time across the whole text, so a later pass rewrites a placeholder name that appears inside an already-substituted, quoted value, breaking out of the quote.
**Steps:**
1. Create an environment whose destination path contains a placeholder name and a payload, e.g. `A__VIRTUAL_NAME__B/x'$(touch PWNED)'y`.
2. Generate the activation script; the VIRTUAL_ENV line renders as `VIRTUAL_ENV='/tmp/t/A'x'"'"'$(touch PWNED)'"'"'y'B/x'"'"'$(touch PWNED)'"'"'y'`.
3. Source the generated `activate` script; `touch PWNED` executes.
4. Same payload executes via `activate.fish`; `activate.xsh` and `bin/activate_this.py` yield Python code execution.
5. A directory named `__VIRTUAL_PROMPT_DISPLAY_TCSH__` breaks `activate.csh` with `Unmatched '''`.
**How found:** Noticed the per-key substitution loop introduced with quoting in 20.26.6 (#2768) rewrites the whole text each pass, so a substituted value containing a later placeholder name gets re-expanded after quote() already ran.
**Exploitation:** Attacker influences the destination path or `--prompt` (CI job or wrapper building path from branch/project name, directory in a cloned repo or unpacked archive); when the victim sources the generated activation script, the injected command runs in their shell.
**Insight:** Multi-pass template substitution that re-scans the whole text after each replacement can re-expand attacker data inside already-escaped literals; test whether any placeholder name can appear in a value, and whether quoting happens before or after all passes.

## Proof of concept (copied exactly from the original report)

**Malicious destination path with placeholder name**

```
`__VIRTUAL_NAME__` holds the environment's directory name, so a destination such as `A__VIRTUAL_NAME__B/x'$(touch PWNED)'y` is enough. In the default `activate` for bash, the `VIRTUAL_ENV` line renders as:
```

**Rendered VIRTUAL_ENV line with injected command**

```
VIRTUAL_ENV='/tmp/t/A'x'"'"'$(touch PWNED)'"'"'y'B/x'"'"'$(touch PWNED)'"'"'y'

Sourcing that script runs `touch PWNED`.
```

