- Unix系で信頼できないリポジトリを
git clone --recursive でクローンすると、CVE-2025-48384 によりリモートコード実行が可能になるおそれがあり、GitとGitを内蔵するソフトウェアの更新が必要
- 原因は、Git設定を読み書きする過程で キャリッジリターン(
\r) の扱いが異なり、検証された値と実際に使われる値が食い違うことにある
.gitmodules のサブモジュール path の末尾に \r を入れると、チェックアウト先のパスが 検証後に別のパス に変わる可能性がある
- Windowsはファイル名に制御文字を許可しないため直接の影響は受けないが、macOSはCVE-2024-32002とCVE-2025-48384の両方の影響を受ける
- パッチでは
\r を含む設定値を引用符で囲むよう変更し、.git 内部ファイルへの書き込みやGitフック作成といった攻撃経路を防いでいる
脆弱性の概要と直ちに取るべき対応
- CVE-2025-48384 は、Unix系プラットフォームで信頼できないリポジトリを
git clone --recursive でクローンした際にリモートコード実行につながる可能性があるGitの脆弱性
- 修正済みのGitバージョン に更新し、GitHub Desktopを含むGit内蔵ソフトウェアもあわせて更新する必要がある
- コマンドラインで
git clone だけを実行した場合、サブモジュールは自動ではクローンされない
- 緩和策として、まず
git clone を --recursive なしで実行し、.gitmodules が安全か確認してからサブモジュールを初期化する方法がある
- GitHub Desktop はデフォルトで再帰オプション付きでクローンするため、この動作で影響を受ける可能性がある
キャリッジリターンとGit設定ファイル
- キャリッジリターンはASCII文字コード13の Carriage Return で、C文字列では
\r と表される
- Unixは行区切りにLF、つまり
\n のみを使う前提だったが、Windowsや多くのインターネットプロトコルではCR+LF、つまり \r\n が使われる
- Gitの
.ini 形式の設定は、ユーザー設定ファイルだけでなくリポジトリに含まれる .gitmodules にも使われている
- Gitの設定パーサーはDOS改行をサポートするため、文字を読む際に次のように動作する
- 現在の文字が
\r なら次の文字を確認する
- 次の文字が
\n なら \r を捨てて改行として扱う
- 次の文字が
\n でなければ次の文字を戻し、\r 自体を返す
- この処理は 行単位 で適用されるため、ある行がCRで終わっていると、ファイル全体の形式に関係なくそのCRが除去されることがある
読み込みと書き込みの不一致
- Gitは設定ファイルを読むだけでなく、
git config のように設定値を書き込む際にも同じ形式で key = value の組を記録する
- 従来のコードでは、設定値を書き戻す際に次の場合にだけ値を二重引用符で囲っていた
- 値が空白で始まる場合
- 値の中に
; または # がある場合
- 値が空白で終わる場合
- 一方で設定読み込みコードは二重引用符付き文字列をサポートしているため、
write_pair が書いた値を後で再読込すると末尾の \r が落ちる可能性がある
- たとえば設定ファイルに
key = "foo^M" があると、書き戻し後に key = foo^M の形になることがある
- この挙動が信頼できない
.gitmodules の値と組み合わさると、サブモジュールのパス処理が混乱する
サブモジュールパスの混同と影響範囲
- Unix系システムではファイル名に制御文字を入れられるため、
.gitmodules の path にCRを含む値を入れることができる
- 例は次のような形
[submodule "foo"]
path = "foo^M"
- この値がGitの設定コードによって
.git/modules/foo/config に書き込まれると、次のような形になることがある
[core]
workdir = ../../../foo^M
- 検証は
.gitmodules から読み取った信頼できないパスに対してすでに完了している
- その後、設定を再読込する過程で末尾の
\r が除去されると、Gitは検証済みのパスとは異なるパスを使うことになる
- その結果、サブモジュールのクローン中に
path = ... で読まれた場所と、実際に書き込まれ利用される場所が異なる可能性がある
- この原理はCVE-2024-32002に似ている
- CVE-2024-32002 では、サブモジュールで大文字小文字の区別の有無を利用してGitを混乱させた
- CVE-2025-48384 では、ファイルシステムがファイル名内の制御文字を許可している必要がある
- Windowsはファイル名に制御文字を許可しないため、この特定のバグの直接の影響は受けない
- macOSはCVE-2024-32002とCVE-2025-48384の両方の影響を受ける
パッチと攻撃可能性
- パッチ では、
write_pair において値に \r が含まれている場合に文字列を引用符で囲むよう変更した
- 変更内容は、
; または # だけを調べていた条件に '\r' を追加するだけの単純なもの
for (i = 0; value[i]; i++)
- if (value[i] == ';' || value[i] == '#')
+ if (value[i] == ';' || value[i] == '#' || value[i] == '\r')
quote = "\"";
- このパス混同により、サブモジュール内の悪意あるファイルをファイルシステム上のほぼ任意の場所に置くことができ、検証を回避した状態でリポジトリ外のシンボリックリンクをたどることも可能になる
- 最も直接的な悪用方法は、
.git ディレクトリ内にファイルを書き込み、Git hook script を作成して、Gitがフックを実行したときに攻撃者制御のコードを動かすこと
- 別の可能性としては
.git/config の上書きがある
- PoCはまだ公開されていないが、CVE-2024-32002 のエクスプロイトをほぼ些細に変更するだけでよいとされている
- 修正コミットの テスト は攻撃手法への大きな手がかりを与えうる
繰り返されるCR問題と教訓
- キャリッジリターンがGitで問題を引き起こしたのは今回が初めてではない
- 1月にはRyotaKが、キャリッジリターンで偽装できるcredential helperプロトコルの問題を 発見 した
- 2023年にはAndré BaptistaとVítor Pinhoが、設定解析のロジック不備である CVE-2023-29007 を発見した
- この脆弱性はC言語そのものの問題ではなく、ほぼあらゆる言語で起こりうる ロジックエラー に近い
- 共通点は、Git内部コンポーネント間、またはGitと外部プロセス間の プロセス間通信 で発生していること
- HTTPのCRLF injection、request smuggling、SMTP smuggling にも似た構造が見られる
- かつてのインターネットは、ポステルの堅牢性原則である「送るときは保守的に、受け取るときは寛容に」に依存していたが、今では最も合理的な助言とは言えないかもしれない
- このテーマは RFC 9413 でより詳しく扱われている
謝辞と関連修正
- この脆弱性はGit監査の過程で発見された
- 同じリリースでは、深刻度の異なる他のバグもあわせて修正されている
- G-Research Open Source がこの作業を可能にした
まだコメントはありません。