仮想環境の有効化スクリプトは、プレースホルダー名を含むパスからコードを実行します。
### 影響
`ViaTemplateActivator.instantiate_template` は、有効化テンプレート内のプレースホルダーを、キーごとに1つずつ、テキスト全体にわたって置き換えます。既に置換され引用された値に、後続のプレースホルダー名が含まれている場合、後続の処理でその名前が引用符で囲まれたリテラル内に書き換えられます。挿入された値は、独自の引用符を持ち、これにより外側のリテラルが閉じられ、残りのパスがコードとして実行されます。
`__VIRTUAL_NAME__` は、環境のディレクトリ名を含んでいるため、`A__VIRTUAL_NAME__B/x'$(touch PWNED)'y` のようなパスでも問題ありません。bash のデフォルトの `activate` スクリプトでは、`VIRTUAL_ENV` の行は以下のようにレンダリングされます。
```sh
VIRTUAL_ENV='/tmp/t/A'x'"'"'$(touch PWNED)'"'"'y'B/x'"'"'$(touch PWNED)'"'"'y'
```
このスクリプトを実行すると、`touch PWNED` が実行されます。プレースホルダー名を含まないパスの場合、何も実行されません。`activate.fish` も同様にコマンドを実行し、レポートでは `activate.xsh` や `bin/activate_this.py` を通じた Python コードの実行も示されています。`activate.csh` では、プロンプト表示プレースホルダーと同じ名前のディレクトリ(例:`__VIRTUAL_PROMPT_DISPLAY_TCSH__`)があると、`Unmatched '''` というエラーでスクリプトが停止します。
`quote()` 関数を修正しても、この問題を解決することはできません。なぜなら、書き換えは `quote()` が実行された後に発生するため、`shlex.quote` や `repr()` も無効になってしまうからです。
攻撃者は、宛先パスまたは `--prompt` オプションに影響を与える必要があります。例えば、ブランチ名やプロジェクト名からパスを構築する CI ジョブやラッパーツール、またはクローンされたリポジトリ内のディレクトリや展開されたアーカイブなどが考えられます。
この問題は、バージョン 20.26.6 から 21.14.1 までのバージョンに影響を与えます。バージョン 20.26.6 では、テンプレートの値を最初に引用符で囲うように変更され(#2768)、キーごとのループが導入されました。
### 修正
バージョン 21.14.2 で修正されました。有効化スクリプトは、環境のパスまたはプロンプト内のプレースホルダー名をプレーンテキストとして保持するようになりました。上記の例を実行しても、何も実行されません。修正: https://github.com/pypa/virtualenv/commit/602b26623a724827ff171bf11fef4ad186d01f74
### 対策
パスや`--prompt`に`__VIRTUAL_NAME__`、`__VIRTUAL_ENV__`、または`__VIRTUAL_PROMPT__`のようなプレースホルダー名を含まない環境を作成してください。また、自分で作成していないアクティベーションスクリプトを実行する前に、生成されたスクリプトの内容を確認してください。
### 謝辞
GitHub Security LabのKevin Stubbings氏(@Kwstubbs)がこの問題を報告しました。
影響を受けるパッケージ:
- pip (Python) virtualenv: 影響を受けるバージョンは20.26.6以降、21.14.1以下、21.14.2で修正済み。