GHSA-9h9j-4vrj-gf7g: CRLF Injection

Published Sep 30, 2026
·
Updated

Summary

pyvenv.cfg is a line-based format with no escape syntax. PyEnvCfg.write() wrote values verbatim, while PyEnvCfg.readvalues() 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 VIRTUALENVPROMPT 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:

console $ virtualenv --prompt $'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, versioninfo, 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 VIRTUALENVPROMPT. 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 VIRTUALENVPROMPT.

Affected Software

1 affected componentFixes available
pip/virtualenv<=21.7.10
21.7.11

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/virtualenv to a version that resolves this vulnerability.

    Fixed in 21.7.11
  2. Compensating control

    Do not pass externally influenced data as the virtualenv prompt; strip all line-boundary characters recognized by str.splitlines() before using a value as --prompt or VIRTUALENV_PROMPT.

Event History

Sep 30, 2026
Advisory Published
via GitHub·11:53 PM
Data Sourced
via GitHub·11:53 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Who is realistically exposed to this issue?

The relevant exposure is automation or tooling that supplies a virtualenv prompt from data controlled by someone other than the person running the command. Examples include CI jobs that template branch names into the prompt or tooling that derives the prompt from external input.

2

What does an attacker need to control?

An attacker needs to influence the prompt value and include a line boundary in it. The prompt can be supplied through --prompt, the VIRTUALENV_PROMPT environment variable, or configuration input.

3

How can I check whether a generated environment was tampered with?

Inspect its pyvenv.cfg for unexpected values in home, prompt, implementation, version_info, version, executable, command, or virtualenv. In particular, verify that home points to the intended base interpreter location.

4

What can be done if updating is not immediately possible?

Do not pass untrusted or externally derived data into the prompt value. Ensure prompt inputs cannot contain line boundaries before creating the environment.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203