untar_dir() におけるパストラバーサル (Tar Slip)深刻度: Critical、CVSS 3.1 9.8
ベクター (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
影響を受けるバージョン: fastcore <= 2.2.30
修正済み: 執筆時点では未修正
CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory、'Path Traversal')
コンポーネント: fastcore.xtras.untar_dir() → fastcore.xtras._unpack() → shutil.unpack_archive()
高リスクランタイム: Python < 3.14 (Python 3.14 では安全な data tar フィルターがデフォルトになる)
報告者: Rahul Karne
CNA: VulnCheck
fastcore.xtras.untar_dir(fname, dest) は、アーカイブを「dest に」展開する documented helper です。
内部的には、アーカイブを標準ライブラリの shutil.unpack_archive() に 抽出フィルターなし、アーカイブメンバーのパス検証なし、抽出後の包含チェックなしで 渡します。
その結果、メンバーに ../ トラバーサルシーケンスや絶対パスを含む TAR アーカイブは、呼び出し元が fastcore に展開を依頼したディレクトリの 外側 にファイルを書き込むことができます。
untar_dir(fname, dest) がすべてを dest 内に閉じ込めると合理的に期待する呼び出し元は、代わりにプロセスが書き込み可能な任意の場所への任意ファイル書き込みを許すことになります。
untar_dir() を呼び出すプロセスの権限で任意ファイル書き込みが可能です。
何が上書きされるかによって、以下につながる可能性があります:
概念実証では、完全な 書き込み → インポート → コード実行 の連鎖を、無害なデモディレクトリ内でエンドツーエンドで実証しています。
fastcore は fast.ai エコシステム全体の基盤依存関係であり、以下を含みます:
fastainbdevghapi毎月数千万回ダウンロードされています。
untar_dir() は、一般的な「アーカイブをダウンロードして展開する」ワークフローの背後にある抽出プリミティブであり、fastai の untar_data と同じ一般的なパターンに従っています。
内容が完全に信頼されていないアーカイブを untar_dir() に渡すコードパスはすべて露出しています。
脆弱なコードは fastcore/xtras.py に存在します:
def _unpack(fname, out):
import shutil
shutil.unpack_archive(str(fname), str(out)) # <-- no filter, no validation
ls = out.ls()
return ls[0] if len(ls) == 1 else out
def untar_dir(fname, dest, rename=False, overwrite=False, uid=-1, gid=-1):
"untar `file` into `dest` ..."
import tempfile, shutil
dest = Path(dest)
with tempfile.TemporaryDirectory() as d:
out = Path(d) / remove_suffix(Path(fname).stem, '.tar')
out.mkdir()
...
src = _unpack(fname, out) # extraction escapes `out` here
...
shutil.move(str(src), dest)
...
return dest
untar_dir() は内部に一時的な抽出ディレクトリを作成します:
<tempdir>/<random>/<archive-stem>/
次に _unpack() を呼び出し、アーカイブを直接以下に転送します:
shutil.unpack_archive()
TAR アーカイブの場合、これは最終的に tarfile.extractall() を呼び出します。
Python < 3.14 では、デフォルトの抽出動作は fully_trusted であり、トラバーサルメンバーが尊重される可能性があります。
例えば、以下のようなアーカイブメンバー:
../../../some/other/dir/file
は抽出ディレクトリからの相対パスで書き込まれるため、それを脱出できます。
fastcore には以下がありません:
filter="data" を渡す。.. パスコンポーネントを拒否する。untar_dir() は shutil.unpack_archive() を安全でサンドボックス化されたアーカイブ抽出器であるかのように扱っています。
そうではありません。
Python の標準ライブラリのドキュメントは、信頼できないアーカイブを抽出すると要求された宛先の外側にファイルが作成される可能性があると警告し、TAR アーカイブには安全な抽出フィルタリングを推奨しています。
fastcore は、サポートする Python バージョンに対して適切な抽出フィルターを渡すことも、同等の包含ガードを実装することもしていません。
また、untar_dir() の呼び出し元にこのセキュリティ上重要な動作について警告していません。
TAR メンバーは任意のパス文字列を含むことができます。
影響を受ける Python バージョンでは、tarfile.extractall() はデフォルトで .. トラバーサルシーケンスを自動的に無効化しません。
したがって、fastcore の一時抽出ディレクトリからの相対パスでトラバーサルメンバーを書き込むだけで、そのディレクトリを脱出し、プロセスが書き込み権限を持つ任意の場所に書き込むことができます。
信頼できるプリミティブは、以下のような相対トラバーサルです:
../../../target/file
これが概念実証で使用されている手法です。
危険な入力は アーカイブの内容 であり、開発者が選択した宛先パスではありません。
アーカイブは、以下を含む外部または半信頼の場所から頻繁に取得されます:
したがって、信頼できないまたは半信頼のソースからアーカイブをダウンロードし、その後 untar_dir() を使用して処理するサービスやツールは、到達可能である可能性があります。
PoC には、この配信パスを実証するために、攻撃者が制御する HTTP エンドポイントから細工されたアーカイブを提供するバリアントも含まれています。
悪用を成功させるには以下が必要です:
アプリケーションが、内容が攻撃者の影響下にあるアーカイブ (アップロード、ダウンロード、その他の信頼できないアーカイブなど) に対して fastcore.xtras.untar_dir() を呼び出す。
ランタイムが Python < 3.14、または安全な TAR 抽出フィルターが適用されない別の構成を使用している。
任意ファイル書き込みからコード実行への昇格には、プロセスが後でトラバーサルで到達可能な場所からファイルをインポートまたはロードする。例えば:
任意ファイル書き込みおよび設定/データの変更には、条件 1 と 2 のみが必要です。
poc_fastcore_cna_demo.py は 実際の fastcore.xtras.untar_dir() API を呼び出します。
デモンストレーションは意図的に無害です。
PoC によって作成されるすべてのファイルは、以下を使用して指定された単一のデモディレクトリ内に留まります:
--demo-root
PoC は実際のシステムファイルを変更せず、クラウドサービスや認証情報にアクセスしません。
環境の詳細と、_unpack() および untar_dir() の脆弱なソースコードを出力します。
以下を含むデモンストレーション用レイアウトを作成します:
service_app/plugins/
以下を作成します:
attacker_supplied_upload.tar
これには、意図された抽出ルートの外側を指すように設計されたアーカイブメンバー (トラバーサルメンバーや以下を含む) が含まれます:
service_app/plugins/startup_hook.py
def auto_extract_uploaded_archive(received_archive):
return untar_dir(
received_archive,
intended_root,
overwrite=True
)
ファイルが意図された抽出ディレクトリの 外側 に書き込まれたことを検証します。
アーカイブによって書き込まれた以下をインポートして、サービスのリロードをシミュレートします:
startup_hook.py
無害なスタートアップフックは以下を作成します:
SERVICE_RELOAD_MARKER
これにより、完全な連鎖が実証されます:
Attacker-controlled archive
↓
Path traversal during extraction
↓
Arbitrary file write
↓
Write into importable / auto-loaded location
↓
Application loads attacker-controlled file
↓
Code execution
影響を受けるパッケージをインストールします:
pip install fastcore
この問題は以下で確認されています:
fastcore 2.2.30
デモンストレーションは以下のいずれかに対して実行できます:
fastcore のコピー--source を使用して選択されたローカルに展開されたソースツリーすべての PoC の書き込みは、--demo-root で指定されたディレクトリ内に留まります。
以前の PoC 出力をクリーンアップします:
python poc_fastcore_cna_demo.py --demo-root ./poc_fastcore --clean
デモンストレーションを実行します:
python poc_fastcore_cna_demo.py \
--demo-root ./poc_fastcore \
--source latest \
--no-pause
省略された出力例:
fastcore version: 2.2.30
Outside marker exists after extraction? True
Is outside marker inside intended extraction root? False
Relative traversal marker exists after extraction? True
Is relative traversal marker inside intended extraction root? False
Service plugin file exists after extraction? True
Service reload marker exists? True
POC WORKED: fastcore extracted attacker-controlled tar members outside the intended root.
サポートされている Python バージョンでは、以下のような安全な抽出フィルターを使用してください:
filter="data"
shutil.unpack_archive() または対応する tarfile 抽出操作と共に使用します。
抽出前にすべてのアーカイブメンバーをチェックする必要があります。
拒否するもの:
.. を含むパスリンクターゲットも抽出前に解決および検証する必要があります。
書き込む前にすべての宛先パスを解決し、意図された抽出ルートの下に留まっていることを検証します。
概念的には:
resolved_member_path
↓
must remain inside
↓
resolved_extraction_root
解決された宛先が抽出ルートを脱出する場合、抽出は失敗する必要があります。
リグレッションテストは少なくとも以下をカバーする必要があります:
../ traversal
absolute paths
Windows drive-letter paths
UNC paths
symbolic links
hard links
untar_dir() が信頼できないアーカイブを安全に処理することを意図しているかどうかを明確に文書化します。
安全でない抽出が意図的にサポートされている場合でも、安全な動作がデフォルトであるべきです。
安全でない動作は、アーカイブの内容を暗黙的に信頼するのではなく、明示的なオプトインを必要とすべきです。
パッケージレベルの修正がリリースされるまで:
untar_dir() に直接渡さないでください。根本的な技術的欠陥は、アーカイブパストラバーサルによって引き起こされる任意ファイル書き込みの脆弱性です。
ただし、最終的な深刻度はデプロイ環境と untar_dir() がどのように露出しているかに依存します。
代表的なベクターは以下に近づく可能性があります:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
結果:
9.8 — Critical
悪用に開発者が手動でアーカイブをダウンロードして抽出する必要がある場合、ユーザーインタラクションと攻撃の複雑さが高くなる可能性があります。
したがって、結果として得られる CVSS スコアは実質的に低くなります。
この問題を評価する際には、2 つの重要な要素を考慮する必要があります。
これは、以下に関連する長く知られている安全でない TAR 抽出脆弱性クラスの一例です:
CVE-2007-4559
現代の Python リリースでは、より安全なアーカイブ抽出動作が導入されています。
Python 3.14 では、安全な data 抽出フィルターがデフォルトになります。
したがって、主な影響を受ける環境は Python < 3.14 です。
ただし、これらのランタイムは依然として広くデプロイされています。
この脆弱性は、呼び出し元が内容が攻撃者によって制御されているか、その他の信頼できないアーカイブを untar_dir() に提供した場合に悪用可能になります。
したがって、このレポートではアーカイブの内容を信頼できないセキュリティ境界として扱います。
堅牢なアーカイブ抽出ヘルパーは、アーカイブがどのように取得されたかに関係なく、安全に失敗する必要があります。
以下のプレースホルダーの日付を実際の開示日付に置き換えてください。
YYYY-MM-DD — メンテナーに脆弱性を報告。YYYY-MM-DD — ベンダーの応答 / 確認 / 応答なし。2.2.30) で依然として未修正であることを確認。抽出コードは変更されていない。発見および報告: Rahul Karne。
fastcore untar_dir() ドキュメント
https://fastcore.fast.ai/xtras.html
fastcore xtras.py ソース
https://github.com/AnswerDotAI/fastcore/blob/main/fastcore/xtras.py
PyPI の fastcore
https://pypi.org/project/fastcore/
Python shutil.unpack_archive() ドキュメント
https://docs.python.org/3/library/shutil.html#shutil.unpack_archive
Python tarfile 抽出フィルター
https://docs.python.org/3/library/tarfile.html#extraction-filters
PEP 706 — tarfile.extractall のフィルター
https://peps.python.org/pep-0706/
CWE-22 — Improper Limitation of a Pathname to a Restricted Directory
https://cwe.mitre.org/data/definitions/22.html
CVE-2007-4559 — 安全でない TAR 抽出脆弱性クラス
https://nvd.nist.gov/vuln/detail/CVE-2007-4559
このリポジトリは、協調開示されたセキュリティ上の発見を文書化し、無害で自己完結した概念実証を提供します。
PoC は、ユーザーが指定した --demo-root ディレクトリ内にのみ書き込み、破壊的な操作を行いません。
デモンストレーションに含まれるネットワーク機能は、オプションのローカルデモンストレーションに限定されています。
この資料は、防御的なセキュリティ研究および教育目的で提供されています。
メディアからの問い合わせ: [email protected]。完全な PoC (攻撃者サーバー、トラバーサル アーカイブ、被害者アプリケーション) および追加の技術的詳細は、要求に応じて入手可能です。