FlawOpen

flawopen.com/AI Security/plugin4shell-ai-agent-plugin-sha-pin-bypass

● CWE-345 · 高
セキュリティ研究 · FlawOpen

Plugin4Shell:プラグインSHA固定の回避

4種類のAIエージェント用プラグインインストーラーは、Gitリビジョンを固定してもcheckout後のcommitを検証しませんでした。Plugin4Shellの教訓は最終SHAの確認です。[1]

💡 わかりやすい解説 (ELI5)

図書館が本の特定の版を固有の蔵書番号で取り置きします。係員はその番号を指定しますが、棚札に同じ文字が書かれていると別の本を渡してしまいます。SHA ピンは蔵書番号です。インストーラーはプラグインを信頼したり実行したりする前に、実際に checkout された commit を確認する必要があります。[1]

主要な概念と専門用語

Commit SHA
特定の Git commit オブジェクトを識別するハッシュです。安全なピン留めでは、解決された checkout とこの値を比較します。[1], [2]
Git ref の曖昧性
Git はオブジェクト ID とブランチ名などをリビジョン式として扱います。条件によっては、commit ID のような名前が ref として解決されます。[1]
プラグインマーケットプレイス
エージェント拡張を配布し、審査済みリビジョンを記録するカタログです。ピンを強制する責任はインストール側にもあります。[1]
CWE-345
データの真正性検証不足です。システムが、データが期待された真正な値であるかを十分確認せずに利用します。[5]

フレームワーク対応

  • このガイドで説明する弱点の CWE 参照: CWE-345

ステップ・バイ・ステップの攻撃フロー

ステップ 1

マーケットプレイスが審査済み commit を記録

プラグインの登録情報にはリポジトリと特定の commit SHA があり、インストーラーはその値を使う想定です。[1]

ステップ 2

リポジトリの管理者が ref を変更

AIR Security によると、そのような名前を許可する Git ホストでは、リポジトリ管理者が要求された SHA と同じ名前のデフォルトブランチを作成できます。Gemini CLI については FETCH_HEAD ref を使う別経路も報告されています。[1]

ステップ 3

インストーラーは HEAD を検証せず checkout

修正前の Codex は要求された SHA を <code>git checkout</code> に渡し、Git の終了成功をピン遵守の証拠として扱っていました。[1], [2]

ステップ 4

クライアントが取得したツリーを受け入れるか拒否

Codex の修正は checkout 後に <code>git rev-parse HEAD</code> を読み、不一致なら中断します。同等の確認がなければ、置き換えられたプラグインコードがエージェント利用者の権限でインストール・実行される可能性があります。[2], [1]

ソースコード比較:脆弱 vs 堅牢化

脆弱な経路は、名前の解決に成功したことをオブジェクトの同一性の証明と取り違えていました。修正は checkout 後の HEAD をマーケットプレイスのピンと比較し、別の commit の展開を拒否します。このコードは Codex の修正を示しています。他製品は別実装のため、同じ修正があると推定してはいけません。[2], [6]

脆弱な実装
// OpenAI Codex PR #34644 の修正前の形
if let Some(target) = sha.or(ref_name) {
    run_git(&["checkout", target], Some(destination))?;
}
// checkout の成功を SHA 一致の証明として扱っていた。
堅牢化されたセキュアパッチ
// Codex の修正:要求引数だけでなく、解決後の commit を検証する
if let Some(sha) = sha {
    run_git(&["checkout", sha], Some(destination))?;
    let checked_out_sha =
        run_git_output(&["rev-parse", "HEAD"], Some(destination))?;
    if !checked_out_sha.eq_ignore_ascii_case(sha) {
        return Err(format!(
            "checked out Git SHA {checked_out_sha} does not match requested SHA {sha}"
        ));
    }
} else if let Some(ref_name) = ref_name {
    run_git(&["checkout", ref_name], Some(destination))?;
}

エンジニアリング&システム堅牢化チェックリスト

参考資料

← AI Security Directory →