Dulwich: stashの適用時に発生するシンボリックリンクによるディレクトリトラバーサルにより、悪意のあるリポジトリから任意のファイルを書き込める脆弱性
## 概要
Dulwichの`stash.py:pop()`関数には、シンボリックリンクによるディレクトリトラバーサルの脆弱性があり、攻撃者が悪意のあるリポジトリから、被害者がstashを適用する際に、リポジトリの作業ディレクトリ外に任意のファイルを書き込める可能性があります。
## 根本原因
`pop()`関数(`dulwich/stash.py:236行目)は、stashされたファイルを書き込む前に、親ディレクトリが存在するかどうかを`os.path.exists(parent_dir)`で確認しています。`os.path.exists()`はシンボリックリンクを辿るため、パス上の途中のディレクトリがシンボリックリンクで作業ディレクトリの外を指している場合(例:`link → ../../.git/hooks`)、チェックはパスし、その後のファイル書き込みがシンボリックリンクを通じて行われます。
`validate_path()`関数(228行目)は、パスの構成要素の名前を`INVALID_DOTNAMES`と比較するだけで、ファイルシステムのシンボリックリンクの検出は行っていません。Dulwich 1.2.7(最新リリース)では、`build_file_from_blob()`にはシンボリックリンクに対する保護機能が一切ありません。
## 影響
攻撃者は、被害者がそのリポジトリをクローンし、stash適用操作を実行した際に、攻撃者が制御する内容を任意のファイルシステム上の場所に書き込むことができる悪意のあるリポジトリを作成できます。`.git/hooks/post-checkout`への書き込みは、被害者のマシン上で、次のgit checkout操作時にリモートコード実行を達成します。
## 攻撃シナリオ
1. 攻撃者は、`main` ブランチに `link` (シンボリックリンク → `../../.git/hooks`) を、`feature` ブランチに `link/post-checkout` (実行可能なペイロード) を含むリポジトリを作成します。
2. 攻撃対象者はリポジトリをクローンします (作業ディレクトリに `main` ブランチがチェックアウトされ、シンボリックリンク `link` が存在します)。
3. 攻撃対象者は `feature` ブランチをチェックアウトし、変更を行い、`stash.push()` を実行します。
4. 攻撃対象者は `main` ブランチをチェックアウトします (シンボリックリンク `link` が復元されます)。
5. 攻撃対象者は `stash.pop(0)` を実行します — stash には `link/post-checkout` が含まれています。
6. `os.path.exists("link")` は True を返します (既存のディレクトリへのシンボリックリンク)、`os.makedirs` はスキップされます。
7. `build_file_from_blob(blob, mode, "link/post-checkout")` → `open("link/post-checkout", "wb")` は、中間シンボリックリンクに従い、ペイロードが `.git/hooks/post-checkout` に書き込まれます。
8. 次のチェックアウト操作により、フックがトリガーされます → 任意のコード実行 (RCE)
## 推奨される修正
ファイルを書き込む前に、ターゲットパスのどの部分も、作業ディレクトリ外のシンボリックリンクを介して解決されないことを確認してください。`os.path.realpath(parent_dir)` を使用し、それがリポジトリのルート内に留まることを確認します。または、各パスコンポーネントに対して `os.open()` を `O_NOFOLLOW` オプション付きで使用します。
報告者: **zx (Jace)**
影響を受けるパッケージ:
- pip (Python) dulwich: 影響あり。バージョン >= 0.22.5、<= 1.2.7。1.2.8 で修正済み。