Résumé
virtualenv writes prompt values into pyvenv.cfg without sanitizing line boundaries, allowing configuration injection
Détails de l’avis
Summary
pyvenv.cfg is a line-based format with no escape syntax. PyEnvCfg.write() wrote values verbatim, while PyEnvCfg._read_values() parses the file with str.splitlines(). A value containing a line boundary therefore became additional configuration lines, and because reading is last-wins, the injected keys replaced any key written earlier in the file.
Impact
The prompt value is the reachable input: it is set by --prompt, by the VIRTUALENV_PROMPT environment variable, or from the config file, and write() emits prompt before home. A crafted prompt can therefore set home in the generated pyvenv.cfg:
$ virtualenv --prompt #39;x"\nhome = /attacker/path\nprompt = "z' venv
$ grep '^home' venv/pyvenv.cfg
home = /attacker/path
home is what tooling reads to locate the base interpreter, so a consumer that trusts it can be pointed elsewhere. implementation, version_info, version, executable, command and virtualenv are also written before prompt and can be replaced the same way.
This requires the prompt to come from somewhere other than the person running the command, for example a CI job templating a branch name into it, tooling deriving an environment name from user-supplied data, or an inherited VIRTUALENV_PROMPT. Where the operator supplies the prompt directly they already control the command line, and the effect is corruption rather than privilege gain: the value is truncated at the boundary and read back with a dangling quote.
Details
The boundary set is the one str.splitlines() recognizes, which is wider than \n: \r, \v, \f, the file, group and record separators, U+0085, U+2028 and U+2029 were all written through unchanged and all split the line when read back.
Patches
PyEnvCfg.write() now collapses those boundaries to spaces as it serializes each line, so it cannot emit a structurally invalid file regardless of what a caller places in content.
Workarounds
Do not pass externally influenced data as the virtualenv prompt. Strip line boundaries from any value before using it as --prompt or VIRTUALENV_PROMPT.
Références
- https://github.com/advisories/GHSA-9h9j-4vrj-gf7g
- https://github.com/pypa/virtualenv/security/advisories/GHSA-9h9j-4vrj-gf7g
- https://nvd.nist.gov/vuln/detail/CVE-2026-102938
- https://github.com/pypa/virtualenv/pull/3247
- https://github.com/pypa/virtualenv/commit/a30f995461043acb6cacbf3a890951563ccf7140
- https://github.com/pypa/advisory-database/tree/main/vulns/virtualenv/PYSEC-2026-4012.yaml
- https://github.com/pypa/virtualenv/releases/tag/21.7.11
- https://pypi.org/project/virtualenv
Vulnérabilités liées
Tout Supply chain →- MEDIUMCVE-2026-41504
Coraza: Native audit-log format allows CRLF injection and log forgery via request body and header fields
- MEDIUMCVE-2026-74866
@fastify/busboy vulnerable to CRLF injection via multipart Content-Disposition filename and name
- HIGHCVE-2026-61815
zbateson/mail-mime-parser has CRLF header injection via attachment filename
- CRITICALCVE-2026-84372
Predis: Redis command injection and denial of service via CRLF smuggling in pipelined commands on aggregate connections
- HIGHCVE-2026-77634
CakePHP: SmtpTransport vulnerable to CRLF header injection
- MEDIUMCVE-2026-84379
HTTPX2: Multipart part header injection via unvalidated file Content-Type and custom headers