Oct 1, 2026·6 min read·2 visits
virtualenv prior to 21.7.11 is vulnerable to configuration injection. By injecting unicode line-boundary characters into the prompt configuration parameter, an attacker can override the home configuration key, redirecting downstream python executions to a malicious binary.
An input validation vulnerability in the virtualenv package allows local execution hijacking via configuration injection. When generating the pyvenv.cfg configuration file, user-controlled parameters such as prompt options are written verbatim without sanitizing line-boundary sequences. This allows attackers to inject arbitrary configuration options, causing the tool to read malicious base interpreter paths.
The Python virtualenv tool isolates dependencies by generating standalone virtual environments, each containing its own package repository and configuration metadata. The core configuration is maintained in a flat, line-delimited key-value configuration file named pyvenv.cfg located in the root of each virtual environment.
This configuration file dictates parameters such as the environment's base interpreter directory, system package access, and the active environment prompt string. The vulnerability surfaces when virtualenv processes user-controlled parameter values, including --prompt or environment variables, during initialization.
Prior to version 21.7.11, virtualenv did not validate or sanitize line boundaries in these user-defined configuration fields. This oversight creates an attack surface where a local attacker or automated process supplying parameters can inject configuration directives, resulting in unauthorized control over the environment's execution parameters.
The root cause of CVE-2026-102938 lies in a mismatch between how configuration parameters are serialized to disk and how they are parsed during read operations. When generating the environment, the PyEnvCfg.write() method writes prompt strings and other parameters directly to pyvenv.cfg without validating the absence of line boundaries.
Conversely, when reading these configurations, the parser PyEnvCfg._read_values() reads the raw text and utilizes Python's built-in str.splitlines() method to isolate lines. Python's str.splitlines() function is designed to handle multiple Unicode newline boundaries. It splits strings on ten distinct character sequences, including line feed \n, carriage return \r, vertical tab \v, form feed \f, unit separators, next line \x85, and Unicode paragraph separators.
By injecting any of these ten recognized characters into a prompt configuration field, an attacker can prematurely terminate the current configuration entry and introduce new, independent lines. Because virtualenv parses files sequentially and retains the last encountered value for duplicate keys, an attacker can append a second home directive. This overrides the legitimate base directory path defined earlier in the file.
Let us examine the differences in the implementation of the configuration serialization between vulnerable and fixed releases. In vulnerable versions of virtualenv, configuration parameters are written sequentially without validation.
# Vulnerable serialization in PyEnvCfg.write()
def write(self):
with open(self.path, "w", encoding="utf-8") as f:
for key, value in self.values.items():
# Values are written directly, allowing embedded newlines to escape
f.write(f"{key} = {value}\n")To address this security flaw, the maintainers introduced a strict sanitization helper called _one_line inside src/virtualenv/create/pyenv_cfg.py. This utility matches all ten Unicode line-terminating sequences recognized by the interpreter's splitting logic and replaces them with a single space character, neutralizing potential configuration injections.
# Patched implementation in virtualenv 21.7.11
from typing import Final
_LINE_BOUNDARIES: Final[tuple[str, ...]] = (
"\n",
"\r",
"\v",
"\f",
"\x1c",
"\x1d",
"\x1e",
"\x85",
chr(0x2028), # Unicode line separator
chr(0x2029), # Unicode paragraph separator
)
def _one_line(text: str) -> str:
"""Replaces line boundaries with a space to prevent configuration injection."""
for boundary in _LINE_BOUNDARIES:
text = text.replace(boundary, " ")
return text
# Secure writing inside PyEnvCfg.write()
# Applying the validation function to both keys and values prevents line escapes
line = f"{_one_line(key)} = {_one_line(normalized_value)}"This defensive approach is robust because it directly mirrors the line-splitting logic of str.splitlines(). By neutralizing the exact set of delimiters parsed as new lines, the patch guarantees that a value cannot be structured to define multiple config attributes.
The exploitation flow depends on an attacker's ability to influence the configuration parameters of a virtual environment during its initialization phase. This vector is relevant in automated contexts, such as multi-tenant hosting platforms or CI/CD pipelines where repository names or branches are used to populate the environment's prompt string.
To initiate the attack, the adversary passes a crafted argument to the --prompt CLI flag, containing an inline carriage return or line separator followed by a spoofed home key. When the file is written, the raw byte sequence formats the configuration file such that the parser interprets it as two separate attributes.
home = /usr/bin
include-system-site-packages = false
version = 3.10.12
prompt = isolated_env
home = /tmp/attacker_controlled_binaries
prompt = final_stubWhen a downstream agent executes the Python virtual environment, the loader parses pyvenv.cfg. The second home entry is read last, overriding the legitimate path. The loader then redirects directory searches for core library dependencies and interpreter execution to /tmp/attacker_controlled_binaries, executing a payload.
The impact of CVE-2026-102938 is focused on environment integrity and local privilege execution control. An attacker who successfully hijacks the virtual environment's configuration can execute arbitrary binaries whenever the environment is activated or called by automated build steps.
This vector is relevant in cloud build servers and continuous integration systems. In these environments, automated tasks often generate virtual environments programmatically using input derived from unverified external Git commit metadata, tags, or pull request payloads. This enables remote injection vectors leading to execution context hijacking.
According to the CVSS v4.0 evaluation, the vulnerability carries a score of 5.8 with local attack vector characteristics. The rating reflects low confidentiality impact, but high integrity impact due to the redirection of the virtual environment's underlying Python execution framework.
Remediating CVE-2026-102938 requires updating the virtualenv library across all build hosts, local environments, and orchestration servers. The maintainers have addressed the issue in version 21.7.11 and all subsequent releases.
For systems where an immediate upgrade is unfeasible, developers should enforce strict input filtering of configuration strings in wrapper scripts. Scripts invoking virtualenv must sanitize input variables to strip the ten recognized Unicode line boundaries before passing parameters to the command line.
Security teams can identify compromised environments by scanning systems for anomalous configuration layouts. A validation tool can read pyvenv.cfg files and verify that critical parameters like home are not defined multiple times within the configuration file, signaling an ongoing injection attempt.
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:P/VC:L/VI:H/VA:L/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
virtualenv pypa | < 21.7.11 | 21.7.11 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-93 |
| Attack Vector | Local |
| CVSS v4.0 Score | 5.8 |
| EPSS Score | 0.00043 |
| Impact | High Integrity, Low Confidentiality, Low Availability |
| Exploit Status | none |
| KEV Status | Not Listed |
The application copies an input string containing CRLF or other line-delimiter characters into a line-oriented output stream or file without sanitization, allowing the alteration of the logical structure.
CVE-2026-102930 is a high-severity supply chain vulnerability in virtualenv prior to version 21.7.12. The download_wheel() function lacks integrity checks when dynamically fetching seed packages (such as pip or setuptools) over the network. This allows an attacker to intercept the network stream, replace packages with backdoored components, and achieve arbitrary code execution inside newly spawned virtual environments.
An algorithmic complexity vulnerability in the `league/commonmark` library's GitHub Flavored Markdown (GFM) Table extension allows remote, unauthenticated attackers to cause a Denial of Service (DoS) via high CPU consumption. By submitting a specially crafted Markdown payload containing an extremely long paragraph without letter characters or pipe symbols, the parser executes a quadratic-time $O(N^2)$ scanning operation that exhausts system resources.
An authenticated Server-Side Request Forgery (SSRF) and credential exfiltration vulnerability exists in LiteLLM proxy prior to versions 1.88.6 and 1.96.2. By bypassing sanitization logic through nested form-data parameters or using connection health checks, authenticated users can redirect outbound API calls to arbitrary endpoints, exposing sensitive upstream administrative credentials.
CVE-2026-91776 is a high-severity Denial of Service (DoS) vulnerability in the FasterXML jackson-databind library. The vulnerability is caused by uncontrolled resource consumption (CWE-400) where raw, unrecognized polymorphic type IDs are cached indefinitely without boundaries inside TypeDeserializerBase. When name-based polymorphic deserialization is configured with a fallback mechanism (such as a default implementation or custom problem handlers), remote attackers can send crafted payloads containing unique unknown type IDs, causing heap exhaustion, Garbage Collection (GC) overhead limit exhaustion, and an Out-of-Memory (OOM) crash.
An uncontrolled resource consumption vulnerability in FasterXML jackson-databind allows remote unauthenticated attackers to cause a Denial of Service (DoS) via crafted JSON payloads containing out-of-order forward references in identity-enabled collections or maps.
A security vulnerability in league/commonmark versions 1.3.0 through 2.10.1 allows remote attackers to bypass Stored Cross-Site Scripting (XSS) protections in the DisallowedRawHtml extension. Due to an validation logic flaw in the regular expression parser, specifically handling bare, unclosed HTML blocks ending at the string boundary, raw HTML tags can be passed to the rendered output. When combined with browser-side parsing heuristics, an attacker can execute arbitrary JavaScript in the context of the user session.