GitHub.comとGitHub Enterprise Server(GHES)に、認証済みユーザーがgit push一発でバックエンドサーバー上の任意コードを実行できる重大脆弱性「CVE-2026-3854」が見つかった。発見者はクラウドセキュリティ企業Wiz Researchで、AIを活用したリバースエンジニアリングでGitHubの内部git proxy「babeld」のサニタイズ漏れを突き止めた。GitHub.comは2026年3月4日に修正済みだが、GHESは公開時点で88%が未パッチのままだ。
この記事ではGitHubの内部gitパイプラインに発生したRCE脆弱性CVE-2026-3854を解説します。サプライチェーン攻撃全体の対策はサプライチェーンセキュリティ2026|攻撃手法・防御ツール・実践チェックリストをご覧ください。
この記事のポイント
git push -oのpush option値にセミコロンを混ぜるだけで、内部のX-Statヘッダーに任意フィールドをインジェクションできる設計欠陥。rails_env・custom_hooks_dir・repo_pre_receive_hooksの3フィールド連鎖で、サンドボックスを無効化したまま任意バイナリを実行できる。- GHESは6バージョン系列で個別パッチ提供。3.14.25 / 3.15.20 / 3.16.16 / 3.17.13 / 3.18.8 / 3.19.4以上に即アップグレード推奨。
CVE-2026-3854とは:git push一発でGitHubサーバーが落ちる仕組み
CVE-2026-3854は、GitHubの内部gitパイプラインに存在するコマンドインジェクション系の脆弱性(CWE-77:Improper Neutralization of Special Elements used in a Command)だ。Wiz Researchの発表によれば、CVSS基本スコアは8.7(Critical)で、必要なのは「標準のgitクライアントと、認証済みのpush権限」だけ。ゼロデイ依存もない。
GitHubはユーザーのgit pushを以下のような多段パイプラインで処理している。
| サービス | 役割 |
|---|---|
babeld |
SSHエントリポイント。git proxyとして認証サービスへリクエストを転送 |
gitauth |
認証情報を検証し、ファイルサイズ制限・ブランチルール等のセキュリティポリシーを返却 |
gitrpcd |
内部RPCサーバー。X-Statヘッダーをパースし、自分自身の認証は行わない |
pre-receive hook |
プッシュを受理する前にセキュリティポリシーを強制実行 |
問題は、babeldがgit push -oで渡されたpush option値をサニタイズせずそのままX-Statヘッダーに連結していたことだ。X-Statはセミコロン区切りのkey=value列で、後段のgitrpcdは「同じキーが複数あったら最後の値を採用する(last-write-wins)」というシンプルな実装になっていた。攻撃者がpush optionにセミコロンを混ぜれば、ヘッダーにある正規のセキュリティ設定を後ろから上書きできてしまう。
# 攻撃者の入力例(push option値にセミコロンを混入)
git push -o 'x;large_blob_rejection_enabled=bool:false' origin master
このコマンドがbabeldを通過すると、内部のX-Statヘッダーは概ね次のように構築される。
X-Stat: client_ip=203.0.113.5;rails_env=production;...;custom_field=x;large_blob_rejection_enabled=bool:false
large_blob_rejection_enabled=bool:falseという偽のフィールドが正規フィールドの後ろに挿入され、last-write-winsによってGitHub側のセキュリティ判定が無効化される。これがCVE-2026-3854の核心だ。
サニタイズ漏れの一行が、世界中の数百万リポジトリを脅かしたという意味で、本件は2026年4月のMCP STDIOトランスポート設計欠陥でRCEが発覚した事例と並ぶ「設計レベルの教訓」になる。
攻撃ベクトル:X-Statヘッダーへのセミコロンインジェクション
X-Statヘッダーの内部仕様は、Wizがリバースエンジニアリングで再構築したものによると、概ね以下のような構造である。
X-Stat: <key1>=<value1>;<key2>=<value2>;<key3>=<value3>;...
key例:
- rails_env … Railsの実行環境(production/development等)
- custom_hooks_dir … カスタムフックの探索ディレクトリ
- repo_pre_receive_hooks … pre-receiveフックの定義(JSON)
- large_blob_rejection_enabled … 大容量blob拒否フラグ
- max_blob_size … blobサイズ上限
- enterprise_mode … Enterprise機能の有効化フラグ
git push -o key=valueで送られたpush optionは、サーバー側でメタデータの一部として保持され、最終的にX-Statに書き込まれる。push optionの値にセミコロンを混入させれば、攻撃者は以後の任意フィールドを書き換え可能——これが全ての出発点になる。
ここで重要なのは「key=value形式の自由度」だ。後段サービスはX-Statを信頼済みメタデータとみなしているため、認証チェックを行わない。つまり、X-Statに書き込めた瞬間、攻撃者は内部APIに対して特権操作を発行する権利を得る。
下の図は、サニタイズ漏れがどこで発生し、どう連鎖するかを示したものだ。
push option由来の文字列を信頼境界の内側で再パースする、というアーキテクチャ自体がアンチパターンだったと言える。
3段階のエクスプロイトチェーン:rails_env, custom_hooks_dir, repo_pre_receive_hooks
実際のRCEには、X-Statに注入する3つのフィールドの連鎖が必要だった。
| 段階 | 注入フィールド | 役割 | 攻撃者の意図 |
|---|---|---|---|
| ① | rails_env=development |
Railsの実行環境を切替 | 本番モードのサンドボックスを回避し、直接実行パスを選ばせる |
| ② | custom_hooks_dir=/tmp/attacker |
カスタムフック探索ディレクトリ | 攻撃者が制御可能なベースディレクトリを指定 |
| ③ | repo_pre_receive_hooks=[{"script":"../../usr/bin/id"}] |
pre-receiveフックのJSON定義 | パストラバーサルで任意バイナリを実行 |
① rails_envでサンドボックスを無効化
pre-receiveバイナリには2つの実行パスがある。productionではサンドボックス内でフックを実行するが、development等の非productionモードでは直接実行される。攻撃者はrails_env=developmentを上書き注入することで、後段のフック実行を非サンドボックスパスに切り替える。
② custom_hooks_dirで探索基点をすり替える
custom_hooks_dirはフックスクリプトを探すベースディレクトリだ。デフォルトはGitHub内部の保護されたパスだが、攻撃者がここを書き換えると、自分が書き込み可能な/tmp配下などに探索パスを向けられる。
③ パストラバーサルで任意バイナリを実行
repo_pre_receive_hooksは通常、JSON配列としてフックスクリプトのリストを定義する。scriptフィールドにパストラバーサル文字列(../../usr/bin/id等)を仕込めば、custom_hooks_dir + scriptの単純連結でファイルシステム上の任意のバイナリへ解決される。フック実行は引数なしでexecされるため、/usr/bin/idや攻撃者が事前に配置したバイナリを直接呼び出せる。
# 概念実装:3フィールド連鎖の同時注入(教育目的・実環境では絶対に試さない)
git push \
-o "x;rails_env=development" \
-o "y;custom_hooks_dir=/tmp/attacker" \
-o 'z;repo_pre_receive_hooks=[{"script":"../../usr/bin/id"}]' \
origin main
最終的に、攻撃者はgitサービスユーザー権限で任意コマンドを実行できる。gitユーザーはバックエンドノード上のリポジトリ全体に広範な読み書き権限を持っているため、ノード内の他テナントのリポジトリにも到達可能だった。これが本件を「クロステナント攻撃」と呼ばせた理由だ。
GitHub.com vs GitHub Enterprise Serverへの影響範囲
最初の発見時点では、エクスプロイトはGHES 3.19.1上では成立する一方、GitHub.com上ではcustom hooksパスに到達できなかったという。Wizの分析チームはここで足を止めず、X-Statにenterprise modeを切り替えるboolean flagが含まれていることをリバースエンジニアリングで特定し、これをセットすることでGitHub.com側でもcustom hooksパスを開通させた。
| 環境 | 影響範囲 | 結果 |
|---|---|---|
| GitHub Enterprise Server (GHES) ≤3.19.1 | サーバー全体 | フルテイクオーバー(全リポジトリ・全管理者権限) |
| GitHub.com | 共有ストレージノード | 同一ノード上の数百万リポジトリにアクセス可能 |
| GitHub.com(クロステナント) | 異なる組織のリポジトリ | 同一ノードに同居する他組織リポジトリも露出 |
GitHubは公式ブログで「フォレンジック調査の結果、脆弱コードパスへの到達はWizのテスト活動のみで、他ユーザー・アカウントによるトリガーは確認されなかった」と述べ、「顧客データへのアクセス・改ざん・流出は発生していない」と結論付けている。とはいえ、GHES側では88%のインスタンスが公開時点で未パッチというデータがWizから示されており、ここからは管理者の責任領域だ。
影響を受けるGHESバージョンと修正バージョン一覧
GitHubは公式アドバイザリで、サポート対象の全バージョン系列に個別パッチをリリースした。下表は公開情報をまとめたものだ。
| バージョン系列 | 脆弱バージョン | 修正バージョン |
|---|---|---|
| 3.14系 | 3.14.24以下 | 3.14.25以上 |
| 3.15系 | 3.15.19以下 | 3.15.20以上 |
| 3.16系 | 3.16.15以下 | 3.16.16以上 |
| 3.17系 | 3.17.12以下 | 3.17.13以上 |
| 3.18系 | 3.18.7以下 | 3.18.8以上 |
| 3.19系 | 3.19.3以下 | 3.19.4以上 |
| 3.20系 | — | 3.20.0以上(最初から修正済み) |
GHESを運用している場合、まずは現在のバージョンを確認する。
# GHES管理シェルからバージョンを確認
ghe-version
# 出力例:
# GitHub Enterprise Server 3.18.5
3.18.5なら3.18.8以上へ、3.19.1なら3.19.4以上へアップグレードする必要がある。本記事の執筆時点でGitHub.com側はすでに修正済みなので、自社で運用するGHESを優先的に対処すべき場面だ。
なお、本件と並行して2026年4月時点ではGitHub Actions経由のサプライチェーン攻撃や、依存パッケージマネージャ経由のRCEも複数報告されている。CIサイドの全体像はRenovate・DependabotとGitHub Actionsを狙うサプライチェーン攻撃の防御策で整理しているので、合わせて読むと効果が高い。
AI支援リバースエンジニアリング:WizのIDA MCP活用
本件のもう一つの注目点は、Wizが採用した「AIエージェント+IDA MCPによるリバースエンジニアリング」という研究手法だ。Wiz自身が公開ブログで「手作業では到達できない速度でコンパイル済みバイナリを解析し、内部プロトコルを再構築した」と述べている。
具体的には、
babeld・gitrpcd等のクローズドな内部バイナリをIDA Pro上で解析- IDA MCPサーバーをClaude/AIエージェントに接続し、関数の意図・データフロー・呼び出し関係をAIに整理させる
- 注入可能フィールドの候補を機械的に列挙し、PoCに繋げる
というワークフローだ。AIに脆弱性を直接見つけさせるというより、「人間アナリストの認知負荷を10倍下げ、調査範囲を10倍広げる」用途と理解すると分かりやすい。MCPサーバーを使った開発者体験そのものは、Anthropic MCP STDIOの設計欠陥でRCEに至った事例でも論点化しており、攻撃側・防衛側の両方でMCP生態系が急速に道具化している。
# Wiz方式に近い、AI+IDA MCPによる関数解析の擬似コード
from ida_mcp import IdaClient
from anthropic import Anthropic
ida = IdaClient(host="localhost", port=9090)
ai = Anthropic()
# babeldバイナリ内のpush option処理関数を抽出
funcs = ida.list_functions(name_pattern="*push_option*")
for f in funcs:
pseudo = ida.decompile(f.address)
resp = ai.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{
"role": "user",
"content": (
"次のC擬似コードでユーザー入力がサニタイズされているか、"
"セミコロン等の特殊文字をどう扱うか、表で整理して。\n\n"
f"```\n{pseudo}\n```"
),
}],
)
print(f.name, resp.content[0].text)
実装そのものはWizが公開していないため、これはあくまで概念例だが、AIが「サニタイズの有無」を関数横断で機械的にレビューするだけでも、設計レベルの脆弱性発見コストは桁違いに下がる。CVE-2026-3854が「2026年中に複数の同種脆弱性が連続発見される」シリーズの1本になる可能性は高い。
検知と対策:監査ログから攻撃を特定する
公開時点で既に修正パッチが配布されているが、過去に攻撃を試みられていないかの遡及調査は別問題だ。GitHubは公式ブログで、GHES管理者に対して/var/log/github-audit.logの監査ログをチェックすることを推奨している。
検知ポイントはシンプルで、push optionにセミコロン;を含むpush操作を洗い出すだけだ。
# GHES監査ログから、push optionにセミコロンが入った操作を抽出
sudo grep -E 'push_options=[^"]*;' /var/log/github-audit.log \
| awk '{print $1, $2, $NF}' \
| sort -u
正規のpush optionにセミコロンが入ることは通常ないため、ヒットすればほぼ確実に攻撃試行と判定できる。検出した場合は、actor・repository・source_ipを確定させ、フォレンジック調査に移行する。
優先順位の付け方は次の通りだ。
| 優先度 | 対応 | 期限の目安 |
|---|---|---|
| 🔴 緊急 | GHESを修正バージョンへアップグレード | 即時〜24時間以内 |
| 🟠 高 | /var/log/github-audit.logを遡及調査(最低90日) |
7日以内 |
| 🟠 高 | カスタムフックディレクトリの権限・所有者を再点検 | 7日以内 |
| 🟡 中 | push権限を持つユーザー・サービスアカウントを棚卸し | 30日以内 |
| 🟡 中 | 不要なpush optionを利用するCI/CDジョブの監査 | 30日以内 |
| 🟢 継続 | WAF/SIEMでX-Stat含むHTTPヘッダーをモニタリング |
継続運用 |
社内CIのなかには、デプロイメタデータをpush optionで送る設計も少なくない。今後はサーバー側だけでなく、自社CIから出ていくpush optionに不正な特殊文字が混じらないかもチェック観点に加えるべきだ。GitHub上のコメント・PR本文を経由した攻撃ベクトルも別軸で広がっており、GitHub Issueコメントを使ったAIエージェントへのプロンプトインジェクション攻撃と合わせて全体像を把握したい。
まとめ:信頼境界を「文字列パース」で跨いではいけない
CVE-2026-3854から得られる教訓は、技術的には地味だが普遍的だ。
- 信頼境界を越える文字列は必ずサニタイズし、構造化された型で運ぶ(key=valueの平文連結ではなくJSONや長さプレフィクス付きフォーマット等)。
- last-write-winsはユーザー入力を含むメタデータには使わない。重複キーは即エラーが安全側。
- マルチサービス間で「同じヘッダー」を共有する場合、各サービスでの再認証・再検証は省略しない。
- セキュリティ研究にAIを組み込んだ攻撃者は、設計欠陥を桁違いの速度で見つける。同じ手法を防御側も導入すべき。
GitHubはGitHub.comを6時間で塞ぎ、GHESにも全系列のパッチを並行リリースした。残るボトルネックはGHES運用者側だ。88%という未パッチ率が示すのは、「クリティカルな修正リリースであっても、運用者がアップグレードを実行しない限り脆弱性は残り続ける」という当たり前の事実だ。本記事を読んだ方は、自組織のGHESバージョンと監査ログを今すぐ確認してほしい。
FAQ
Q. CVE-2026-3854はどのくらい深刻ですか?
A. CVSSスコアは8.7(Critical)。認証済みユーザーが標準のgitクライアントだけで任意コマンドを実行できる点が極めて重い。GHESではgitサービスユーザー権限のRCE、GitHub.comでは同一ストレージノード上の数百万リポジトリへのアクセス、いずれも単発のpushで成立する。
Q. 既に攻撃を受けていないか確認する方法は?
A. GHESの/var/log/github-audit.logからpush_optionsフィールドに;を含む行を抽出してください。本文中のgrepスニペットがそのまま使えます。正規利用でセミコロンが入ることは通常ないため、ヒットすれば攻撃試行とみなして良いレベルです。
Q. GitHub.comユーザーは何かアクションが必要?
A. 必要ありません。GitHub.com・GitHub Cloudは2026年3月4日 19:00 UTCに修正済みで、GitHub公式は「顧客データへのアクセス・改ざん・流出は発生していない」と確認しています。
Q. push optionを利用するCI/CDがあるが、影響はありますか?
A. 攻撃を受ける側ではなく、攻撃の踏み台になりうる観点で見直す価値があります。push optionにユーザー入力(コミットメッセージ、PRタイトル等)を素通しで載せていないか、特殊文字を含むpush optionが混入しないか、CIのパイプライン側で検査してください。
Q. なぜWizはこれほど早く脆弱性を見つけられたのですか?
A. AIエージェントとIDA MCPを組み合わせたリバースエンジニアリング手法を使ったとWizは説明しています。コンパイル済みバイナリの関数群をAIに横断レビューさせることで、サニタイズ漏れの候補を機械的に列挙できる、というアプローチです。今後この手法は防御側にも広がると見られます。
Q. オンプレ環境ではアップグレードに時間がかかります。暫定対策は?
A. 完全な緩和策は修正バージョンへのアップグレードしかありません。やむを得ず時間が必要な場合は、(1) 不要なpush権限の即時剥奪、(2) push操作を行えるネットワーク区画の限定、(3) X-Stat関連リクエストへの異常検知ルールをWAF/プロキシ層で追加、を組み合わせて時間を稼いでください。それでも数日以内にアップグレードする計画を強く推奨します。
参照ソース
- GitHub RCE Vulnerability: CVE-2026-3854 Breakdown — Wiz Blog
- Securing the git push pipeline: Responding to a critical remote code execution vulnerability — The GitHub Blog
- Critical GitHub.com and Enterprise Server RCE Vulnerability Enables Full Server Compromise — Cyber Security News
- Researchers Discover Critical GitHub CVE-2026-3854 RCE Flaw Exploitable via Single Git Push — The Hacker News
- CVE-2026-3854 GitHub flaw enables remote code execution — Security Affairs
- CVE-2026-3854 — Vulners