はじめに
2026年2月、Claude Codeに「sandbox escape via persistent configuration injection in settings.json」という脆弱性(CVE-2026-25725, CVSS 7.7)が報告された1。サンドボックス内から .claude/settings.json にフックコマンドを注入し、次回起動時にホストOS上で任意コード実行につながるという内容で、v2.1.2で修正済みだ1。
セキュリティ企業Cymulateは、この脆弱性を「Configuration-Based Sandbox Escape(CBSE)」という単一ツールに閉じない脆弱性クラスとして整理し、Claude Codeだけでなく Gemini CLI・Codex CLI にも同種の問題が見つかっていることを報告している2。
この記事では、公式のGitHub Security Advisoryと Cymulate のCBSEレポートを一次情報として突き合わせたうえで、本プロジェクト自身の .claude/settings.json の権限設定(permissions.deny)を実際に確認し、CVE-2026-25725と同じ「片方だけ守って片方を忘れる」形の設定漏れが無いかを検証した。
この記事で分かること
- CVE-2026-25725がbubblewrapサンドボックスのどの前提を突いたか
- Gemini CLI・Codex CLIに残る同種の未パッチ問題(CBSE脆弱性クラス)
- 実プロジェクトの
permissions.denyリストを確認して見つかった、同じ形の設定漏れ
TL;DR
- CVE-2026-25725は「存在しないファイルには読み取り専用マウントを適用できない」というbubblewrapの制約を突き、サンドボックス内から
settings.jsonを新規作成してSessionStartフックを注入する脆弱性(CVSS 7.7、v2.1.2で修正済み)1 - 同種の「エージェントが自分で書き込める設定ファイルを後から無条件に信頼してしまう」パターン(CBSE)は Gemini CLI・Codex CLI にも見つかっており、90日以上未パッチのものもある2
- 本プロジェクトの
.claude/settings.jsonを確認すると、settings.local.jsonへのWrite/Editはpermissions.denyで明示的に禁止されているが、本体のsettings.jsonには同種の保護ルールが存在しなかった
検証内容
公開されているGitHub Security Advisory(GHSA-ff64-7w26-62rf)から、CVSSベクター・影響バージョン・修正バージョンを確認した1。あわせてCymulateのCBSEレポートで、同じ脆弱性クラスがどのツールにどう現れているかを確認した2。そのうえで、実際にこのリポジトリで運用している .claude/settings.json の permissions ブロックを読み、CVE-2026-25725が突いた構造(「特定ファイルだけ保護し、その他は無防備」)と同じ形が存在するかを照合した。
発見1: なぜ「存在しないファイル」が守れないのか
CVE-2026-25725の本質は、Claude Codeのbubblewrapサンドボックスが .claude/settings.local.json は読み取り専用マウントで明示的に保護していた一方、settings.json が起動時に存在しない場合はその保護が効かなかった点にある1。
bubblewrapは指定したパスを読み取り専用でバインドマウントできるが、そのパスに実体のファイルが存在しない場合はマウント自体を適用できない。親ディレクトリは書き込み可能な状態でマウントされていたため、サンドボックス内で実行中の悪意あるコードが settings.json を新規作成し、SessionStart フックとして任意コマンドを仕込むことができた。次回Claude Codeを起動すると、このフックはサンドボックスの外=ホスト権限で実行される1。
CVSSベクターは CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N(スコア7.7)で、機密性・完全性・可用性のすべてに高い影響を与える1。根本原因はCWE-501(Trust Boundary Violation)とCWE-668(Exposure of Resource to Wrong Sphere)に分類されている1。この問題は @anthropic-ai/claude-code v2.1.2で修正済みで、自動更新を有効にしているユーザーはすでにパッチが適用されている1。
発見2: 同じ脆弱性クラスがGemini CLI・Codex CLIにも見つかっている
Cymulateはこの種の問題を「Configuration-Based Sandbox Escape(CBSE)」と名付け、OS・コンテナランタイムを直接突破するのではなく、エージェント自身の設定ファイル・起動時の挙動・信頼境界の甘さを悪用する 共通パターンとして整理した2。同レポートによると、対象は3ツールに及ぶ。
| ツール | 状態 | 手口 |
|---|---|---|
| Claude Code | v2.1.2で修正済み(CVE-2026-25725) | 存在しない settings.json の作成→SessionStartフック注入 |
| Gemini CLI | 未パッチ(90日以上経過) |
oauth_creds.json のOAuth認証情報窃取、Windowsでの実行可能ファイル解決(where.exe の先読み)の悪用 |
| Codex CLI | 未パッチ(情報扱いで却下) |
apply_patch ツールがユーザー承認なしに .codex/config.toml を作成し、承認ポリシー自体を無効化 |
3ツールに共通するのは、「エージェントが操作してよい範囲」と「エージェントが二度と手を出せない信頼済み設定」の境界線が、実装上はコードの隅々まで一貫していないという点だ。Claude Codeのケースでは保護対象ファイルを列挙する実装だったため「まだ存在しないファイル」という想定漏れが生じた。Codex CLIのケースでは、パッチ適用ツールという別の正規機能が、承認ポリシーファイルへの書き込み経路として悪用できてしまう。
発見3: 本プロジェクトのdenyリストにも同じ形の穴があった
CVE-2026-25725もCBSEレポートも、根っこにあるのは「エージェントが持つ書き込み権限の対象を、個別のファイル名で列挙して保護しようとすると、列挙漏れが起きる」という設計上の教訓だ。この観点で本プロジェクトの .claude/settings.json の permissions ブロックを確認した。
"permissions": {
"allow": [
"mcp__kinako-mocchi__generate_image",
"mcp__kinako-mocchi__check_status"
],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(**/*.pem)",
"Read(**/*.key)",
"Read(**/credentials*)",
"Read(**/id_rsa)",
"Read(**/id_ed25519)",
"Write(.claude/settings.local.json)",
"Edit(.claude/settings.local.json)"
]
}
deny リストには settings.local.json へのWrite/Editを禁止するルールが明示的に存在する。しかし、フック定義・モデル指定・権限リストそのものを持つ本体の settings.json に対する同種のdenyルールは無い。
これはCVE-2026-25725そのものではない。あちらはOSレベルのbubblewrapマウント保護、こちらはClaude Codeエージェント自身のツール実行を制御する permissions.deny(アプリケーションレイヤーの自己制限)であり、防御の層が異なる。だが「ローカル上書きファイルだけを名指しで守り、本体の設定ファイルは無条件に信頼している」という構造そのものは、CVE-2026-25725やCodex CLIの config.toml ケースと同じ形をしている。settings.json は次回以降のセッションで実行される全フック・全権限設定の起点であるため、ここへの想定外の書き込みが一度でも通れば影響範囲は大きい。
対策として考えられるのは、permissions.deny に本体ファイルへのルールも追加することだ。
"deny": [
"Write(.claude/settings.local.json)",
"Edit(.claude/settings.local.json)",
"Write(.claude/settings.json)",
"Edit(.claude/settings.json)"
]
このプロジェクトはもともと「全変更は作業ブランチ→PR→レビュー経由でmainへ反映する」運用のため、settings.json の変更を人間レビュー必須のPR経路に一本化しても運用上の追加コストはほぼ発生しない。むしろ、エージェントの自律実行ループがこのファイルに直接書き込める状態を放置する理由がないことが、今回の照合で明確になった。
実務での対策
- Claude Code CLIを利用している場合は
claude --versionでv2.1.2以上であることを確認する(自動更新有効なら通常は適用済み)1 - 自作の
permissions.denyリストは「ローカル上書きファイルだけ」でなく「本体の設定ファイル」も同じ強度で保護されているか、実際のJSONを開いて確認する - Gemini CLI・Codex CLIを併用している場合は、OAuth認証情報ファイルや
config.tomlのような「エージェントが自分で書き込める設定ファイル」がその後無条件に信頼されていないかを個別に確認する。CBSEはツール横断のパターンであり、1ツールで対策しても他ツールで再発する2
著者視点の発見ポイント
CVE-2026-25725のレポートを読んでいる時点では「bubblewrapの実装バグの話」だと捉えていたが、実際にこのプロジェクト自身の permissions.deny を開いて確認したところ、防御レイヤーはまったく違うのに構造として同じ穴(ローカル上書きファイルだけ守って本体を忘れる)が存在していた点が最大の発見だった。脆弱性レポートを読むだけで終わらせず、自分たちが運用している設定ファイルに同じ形の想定漏れがないかを照合する作業自体に価値があると実感した。
まとめ
- CVE-2026-25725は「存在しないファイルには読み取り専用マウントを適用できない」というbubblewrapの制約を突いた、Claude Codeのsandbox escape脆弱性(CVSS 7.7、v2.1.2で修正済み)
- 同種のCBSE(Configuration-Based Sandbox Escape)はGemini CLI・Codex CLIにも見つかっており、90日以上未パッチのものがある
- 「ローカル上書きファイルだけ守って本体を無条件に信頼する」構造は、防御レイヤーが違っても再発しうる。自分のプロジェクトの
permissions.denyを開いて確認する価値がある