世界を動かす技術を、日本語で。

キャリッジリターンでGitを破壊し、RCEをクローンする

概要

  • Gitで発見された新たな脆弱性 について解説
  • .gitmodulesファイル経由でリモートコード実行 が可能となる危険性
  • 制御文字(CR)処理の不備 が原因で発生
  • Unix系OSで特に深刻、Windowsは直接影響なし
  • 最新版Gitや関連ソフトへのアップデート推奨

GitのCR(キャリッジリターン)処理脆弱性の概要

  • Git clone --recursive を未信頼リポジトリで実行すると リモートコード実行 のリスク
  • .gitmodulesファイル はサブモジュール情報を管理する 信頼されないファイル
  • 制御文字CR(キャリッジリターン, \r, ^M) の扱いに問題
  • Unix系ファイルシステム はファイル名に制御文字を許容
  • Windowsは制御文字をファイル名に使用不可 のため、直接的な影響なし

技術的詳細と脆弱性の原因

  • Gitの設定ファイル形式 はINI風で [section] key = value 形式

  • Gitのconfigパーサ は行末CRを 無条件で除去

  • 値の書き出し(write_pair関数) では特定条件でのみ値を引用符で囲む仕様

    • 先頭/末尾がスペース、値中に;や#がある場合のみ引用符付与
  • 値の末尾にCRがある場合、再読込時にCRが除去され 値が変化

  • .gitmodules内のサブモジュールpathに制御文字含む場合、検証後にパスが変化し 意図しない場所にサブモジュール展開

    • 例:[submodule "foo"] path = "foo^M" → 検証時はfoo^Mとして扱うが、config再読込時にfooになる
  • 検証と実際の動作でパスが食い違いサブモジュールの内容が予期せぬディレクトリに書き込まれる

影響範囲と対策

  • GitHub Desktop はデフォルトで --recursive オプションを利用

    • 自動的にサブモジュールも取得 し、脆弱性に直面しやすい
  • コマンドラインでの手動clone の場合、 --recursiveを使わずにまず.gitmodulesを確認 することで一時的な回避策

  • macOSはCVE-2024-32002と本脆弱性(CVE-2025-48384)両方に影響

  • パッチ内容 :write_pair関数で値中にCRが含まれていれば 必ず引用符で囲む よう修正

    • 修正前:if (value[i] == ';' || value[i] == '#') quote = """;
    • 修正後:if (value[i] == ';' || value[i] == '#' || value[i] == '\r') quote = """;
  • 最新版GitやGitHub Desktop等の関連ソフトウェアへのアップデート が必須

類似事例・教訓

  • 過去にもCRLFや設定パーサの脆弱性 が複数発見
    • 例:CVE-2024-32002(大文字小文字の扱いミス)
    • 例:2023年 André BaptistaとVítor Pinhoによるロジックエラー
  • 本質的にはプログラミング言語依存ではなく、設計上のロジックミス
  • インタープロセス通信や設定ファイル処理では入力値の厳格な検証が重要
  • CRLFインジェクションやHTTP/SMTPスミッシングと類似性
  • Postelの法則(寛容な入力受容)は現代のセキュリティ観点では再考が必要

まとめと推奨アクション

  • .gitmodulesなど未信頼ファイルの処理には細心の注意
  • 制御文字や特殊文字の扱いに注意
  • 関連ソフトウェアの定期的なアップデート
  • セキュリティ監査や入力検証の徹底
  • 脆弱性詳細やPoCは現時点で非公開だが、修正コミットのテスト等にヒントあり

G-Research Open Source による監査で発見 今後も同様の設計ミスに注意喚起

Hackerたちの意見

もうちょっと待たなきゃいけないみたいだね… Homebrewではまだgit 2.50.1のアップデートが来てない。

https://github.com/Homebrew/homebrew-core/pull/229423

Homebrew自体が問題になってるのかな?再帰的なクローンをするのか?リポジトリをざっと見た感じでは、そうっぽいけどね。

そうじゃなかったら変だよね。Homebrewの目的はリポジトリ内のコードを実行することだから。ここでのRCEが問題になるのは、実行したくないデータを含むGitHubリポジトリをクローンする場合だけだし、それはかなり珍しいことだよ。

これらの結果として、サブモジュールのクローンを実行すると、path = ...から一つの場所を読み取るかもしれないが、^Mで終わらない別のパスに書き出されることがある。この記事が言うように、これが「リモートコード実行」をどう実現するのか?セキュリティの観点から見て、どれくらい深刻なの? > まだPoCは共有してないけど、CVE-2024-32002のエクスプロイトのほぼ trivial な修正だよ。それを修正するコミットには大きなヒントがあるテストも含まれてる。EDIT: CVE-2024-32002から > サブモジュールを持つリポジトリは、Gitのバグを利用するように作成でき、サブモジュールの作業ツリーではなく、.git/ディレクトリにファイルを書き込むように騙されることがある。これにより、クローン操作がまだ実行中の間に実行されるフックを書き込むことができ、ユーザーは実行されるコードを確認する機会がない。だから、リポジトリには悪意のあるgitフックが含まれる可能性がある。通常、gitフックは「git clone」ではインストールされないけど、このエクスプロイトではそうできて、gitフックはクローン操作中に実行されることがある。

新しいCVEに関する詳細はここにあるよ (https://github.blog/open-source/git/git-security-vulnerabili...) : > 設定値を読み取るとき、Gitは末尾のキャリッジリターン(CR)やラインフィード(LF)文字を削除する。しかし、設定値を書き込むとき、Gitは末尾のCR文字を引用しないため、後で読み取るときに失われる。末尾にCR文字を含むパスのサブモジュールを初期化すると、削除されたパスが使用され、サブモジュールが間違った場所にチェックアウトされる。 > もし削除されたパスとサブモジュールのフックスディレクトリの間にシンボリックリンクが既に存在する場合、攻撃者はサブモジュールのpost-checkoutフックを通じて任意のコードを実行できる。その他にも注目すべきgitのCVEがいくつかあるよ。

うん、残念だけどかなり簡単だね。任意のファイル書き込みが可能なときは、RCEも通常可能だよ。

アドホックなDSLを設定に使うのは大きな問題だよね。文法の正式な仕様がないことが多いから、パースの真実の源が手作りのシリアライズ実装とデシリアライズ実装の間に散らばってる。もしそれらが同期しなくなると(例えば、誰かがパーサーに新しい文法を追加したのに、ライターを更新するのを忘れた場合)、パーサーの差異が生じて、タイムボムがカウントダウンを始める。教訓は、真実の源を一つ持ち、それに依存するすべてをそこから生成することだね。

ちょっとした指摘だけど、ここでのDSL(「iniファイル形式」)は一見アドホックに見えるけど、実際にはすごく一般的で理解されてるし、実用的にうまく機能するようなシンプルさがあるんだよね。ここでのバグは形式のせいじゃない。実際に文句言ってるのは、Cプログラムの真ん中に爆弾みたいに座ってる手書きのパーサーのことなんだ。そう、あんなのは何十年も前に消えてるべきだった。Cでも、現代でも、巧妙な手書きコードが必要な場所はあるけど、データのやり取りは絶対にそうじゃない。これだけはやめとけ。もし.iniが欲しいなら、tomlを使え。そうじゃなければJSONを使え。YAMLでもOKだよ。XMLが好きな人もいるけど、もし自分の形式がバイナリじゃなきゃダメだと思い込んでるなら(間違ってるよ、それは必要ない)、protobufがあるからね。でも、絶対に、絶対に、プログラミング言語の作者じゃない限りパーサーを書くな。これにはライブラリを使え、他のことにライブラリを使わなくてもね。[1] ああ、わかった、レキサーね。結局、細かいことを言ってるんだ。

作者の意見には完全に反対だね(コメントでよく引用されてるけど): > これはCで書かれたソフトウェアの根本的な問題ではないから、特に興味深い。これはほぼすべての言語で可能な論理エラーだ。神様のために、チューリングは一つの言語のエラーは他の言語でも可能だと教えてくれたんだ。Rustでも、全体のマシンエミュレーターを構築して、その後VM内でMallocを使う何かを実行すればダブルフリーが起こることもある。Rustや同様のメモリ安全な言語は、Cが作り出す地雷原のような問題を文字通りエミュレートできるけど、論理エラーが「可能」であることは、論理エラーがツールボックスから引き出す最初の道具であることとは大きく異なる。他のコメントでは、C以外の言語ではセキュリティを強化したライブラリを最初に使う可能性が高いと指摘されていて、確かにそれは役立つかもしれないけど…そのコメントへの返信でも、依存関係の地獄に悩まされることになると正しく指摘されてるし、さらに広く依存されているライブラリは新しいエクスプロイトが見つかったときに攻撃面を広げる可能性があるという問題もある。ライブラリは非常に強力なツールだけど、万能ではない。もっとデータ安全な言語(RustやHaskell、LISPなど)が持つ本当の価値は、データをより注意深くモデル化するための組み込みの抽象を提供することにあると思う。単なるオクテットの火ホースのように扱うのではなく、そういう前提を持った言語でコーディングしているときは「パースして、検証しない」ことを守るのがずっと簡単だよ。機械語より少し抽象的なだけの言語で、制御フローのアクションごとにジャンプ命令を使わされることがないことに感謝することができる。

Rustでこのバグが発生するのは簡単に想像できる。どこかの段階でデータモデルをテキストに変換して書き出したり、受信したテキストを解析したりする必要があるからね。行ごとに解析したいなら、BufRead::lines()を使うかもしれないし、その行のためのパーサーを書くことになる。で、そのパーサーはCRには全く触れないから、逆にデータモデルを行にシリアライズするコードを書くとき、末尾にCRを付けないようにするのを忘れやすいんだよね。CRはコードのどこにも出てこないから。

Rustでは、全体のマシンエミュレーターを構築して、その後にVM内でMallocを使う何かを実行すると、ダブルフリーが発生することもあるよ。Rustや同様のメモリ安全な言語は、Cが地雷原にできる問題を文字通りエミュレートできるんだ。それはCとRustのメモリ安全性についての議論には関係ない。エミュレートされたマシンでの無効なメモリアクセスは、ホストシステムの他のプロセスのメモリにはアクセスできないからね。2つの言語がチューリング完全だからって、同じ言語になるわけじゃないし、バグの互換性があるわけでもない。Rustは本当にメモリ安全なプログラムを書くことを可能にしてくれるよ。

あなたが指摘するように、Rustのような「安全な」言語の「安全性」機能を最も深刻に損なう方法は、VMやプログラミング言語、シリアライズ・デシリアライズフレームワークなどを実装することなんだ。これらはRustの型システムやメモリ安全性の外で動作するからね。そして、まさにそれをGitの開発者たちはここでやったわけだ。彼らは社内の設定ファイルフォーマットを作ったんだ。Rustで実装すると、Rustの安全機能、特に型安全性のほとんどをバイパスすることになる。

「既存のエクスプロイトのトリビアルな修正」…なんでgitはLandlockを使わないの?Linux専用なのは知ってるけど、なんで?「git clone」は設定ディレクトリへの読み取り専用アクセスとクローンディレクトリへの読み書きアクセスだけ持つべきだし、サブプロセスもなし。すべてのエクスプロイトデモで:「はい、四角い穴に行って計算機を起動します」って感じだね。

サブプロセスはないから、ポストチェックアウトを含むすべてのGitフックを壊してもいいってことだね。あれはサブプロセスとしての機能だから。もし壊す多くの機能を使ってないなら、セキュリティコンプライアンス付きのコンテナでGit操作を実行することもできるよ。

俺だけかな?このブログのフォント、目が疲れるんだけど。

あなたのコメントを読んだ後、偏見があるかもしれないけど、確かに気になるね。

うん、言いたいことわかる。アンチエイリアスが壊れてるみたいだね。

誰かがCR+LFの文脈でジョン・ポステルを引用しているのを見ると、懐かしい気持ちになるね。* https://jdebp.uk/FGA/qmail-myths-dispelled.html#MythAboutBar... 「今ではそれが最も理にかなったアドバイスとは言えない」とM.リードビータが言ってる。2003年の頃は、もっとはっきり言ってたよね。(-: マーク・クリスピンが当時言ってたように、人々がそれに対して持つ解釈はM.ポステルが同意するものではなかった。1990年代後半、ダニエル・J・バーンスタインが有名な分析を行って、人間が読める形式と機械が読める形式の間で変換する際のパースと引用が問題の原因になることを指摘したんだ。で、今ここにいるわけだけど、25年以上経った今でも、CRを引用しない引用者がいる(修正後もすべてのホワイトスペース文字を探してない)。面白いことに、git blameによると、その問題のコードは19年前に書かれたもので、ダニエル・J・バーンスタインがパースと引用に関する言葉について10年の振り返りをしていた頃だ。* https://github.com/git/git/commit/cdd4fb15cf06ec1de588bee457... * https://cr.yp.to/qmail/qmailsec-20071101.pdf 20世紀に学んだ教訓を繰り返すしかないんだろうね、21世紀でもまだ適用されるし。

Homebrewがまだ脆弱なバージョンを提供しているみたいで、Debian Bookwormも同じだね。