flawopen.com/Teardowns/cve-2024-21626-runc-container-breakout-leaky-vessels
CVE-2024-21626:runc が Leaky Vessels を修正した方法
CVE-2024-21626 は、runc 内部のファイルディスクリプタ漏えいと chdir 後の境界確認不足が組み合わさった問題です。起動経路によって、コンテナがホストの名前空間にある作業ディレクトリで起動したり、関連する exec 経路からホストファイルに到達したりする可能性がありました。[1], [2], [3]
runc はコンテナ用の隔離された部屋を準備します。内部ハンドルの漏えいにより、選ばれた作業ディレクトリが部屋の外を指す可能性がありました。最後の境界確認と内部ハンドルのクローズが、この越境を防ぎます。[1], [2]
主要な概念と専門用語
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-403
根本原因の分析 (Root Cause)
runc 1.1.12 より前では、内部ファイルディスクリプタが runc init に漏れることがありました。これは runc init 内部の漏えいであり、コンテナプロセスに単純に継承されたわけではありません。攻撃1と2では、設定または指定された作業ディレクトリがホストのマウント名前空間を参照するハンドル経由で解決される可能性があり、runc は chdir 後に最終 cwd がコンテナのルート内にあるか確認していませんでした。別の亜種では execve のパス処理を利用し、ホストのバイナリを上書きしました。1.1.12 は Linux の Getwd/ENOENT 動作による cwd 検証、execve 直前の内部ディスクリプタのクローズ、既知の漏えい修正、runc init 起動前の非 stdio ディスクリプタへの close-on-exec 設定という4層を追加しました。[1], [2], [3], [4]
CVE-2024-21626:攻撃バリアントと runc 1.1.12 の対応策
| アドバイザリのバリアント | 必要な条件と境界 | 対応する修正または一時対策 |
|---|---|---|
攻撃1 — runc run による悪意あるイメージ | 特権ユーザーに未信頼イメージを起動させる必要があります。設定された作業ディレクトリがコンテナのマウント名前空間外に解決される場合があります。 | chdir 後の unix.Getwd() 検査は cwd が名前空間ルート外にあると失敗します。一時対策として未信頼イメージの cwd は / のみにします。 |
攻撃2 — コンテナ内プロセスと runc exec | コンテナ内のプロセスが、管理者による runc exec --cwd で / 以外が選ばれることを予測し、実行前に該当パスを準備する必要があります。 | 同じ chdir 後の cwd 検査によりコンテナ外での実行を拒否します。修正前は --cwd を / に制限します。 |
| 攻撃3a — イメージ起動を介したホストバイナリ上書き | 攻撃1を応用したもので、特権ユーザーが悪意あるイメージを起動する条件が残ります。この亜種は execve 境界を越えてホストバイナリに到達します。 | execve の直前に runc の内部ディスクリプタを閉じ、確認済みの漏えいを修正して O_CLOEXEC を設定します。runc 1.1.12 以降へ更新します。 |
攻撃3b — runc exec を介したホストバイナリ上書き | 攻撃2を応用し、攻撃者が影響を与えられる稼働中コンテナの悪意あるプロセスから始まります。ただし、特権を持つ管理者がそのコンテナに対して通常の runc exec を実行する必要があります。アドバイザリによると、イメージ経由よりユーザー操作は少なくて済みます。 | exec 前のクローズと close-on-exec がこの別経路を保護します。メンテナーは実用的な回避策を示していないため、更新してください。 |
ステップ・バイ・ステップの攻撃フロー
影響と条件
アドバイザリは、4つの亜種でホストファイルシステムへのアクセスとホスト制御が可能になると説明しています。イメージ経由の攻撃には、特権ユーザーが未信頼イメージを起動する必要があります。攻撃3bでも、管理者が攻撃者の影響下にあるコンテナで通常の runc exec を実行する必要があります。[1]
ソースコード比較:脆弱 vs 堅牢化
// 修正前の動作を簡略化した表現。runc の逐語的なソースではありません
if err := os.Chdir(config.Cwd); err != nil {
return err
}
// 不足していた処理:最終 cwd が名前空間ルート内か確認する
// runc 1.1.12 の境界確認(upstream 抜粋)
if wd, err := unix.Getwd(); errors.Is(err, unix.ENOENT) {
return errors.New("cwd はコンテナのマウント名前空間ルート外です")
} else if err != nil {
return fmt.Errorf("cwd の安全性を確認できません: %w", err)
} else if !filepath.IsAbs(wd) {
return fmt.Errorf("cwd が絶対パスではありません: %q", wd)
}
エンジニアリング&システム堅牢化チェックリスト
- ✓runc を 1.1.12 またはベンダーがサポートする新しい版へ更新し、各ノードイメージの実効バージョンを確認します。[1], [4]
- ✓未信頼コンテナイメージをコード実行要求として扱い、Docker、Kubernetes、ビルドパイプラインで起動できる権限を制限します。[1]
- ✓ランタイム実装では chdir 後に、プラットフォームが提供する名前空間ルート検査で cwd を確認し、エラー時は安全側に失敗させます。[1], [2]
- ✓内部ディスクリプタに O_CLOEXEC を設定し、ランタイム自身が必要とするものを考慮したうえで、exec 前の最後の安全な時点で非 stdio ディスクリプタを閉じます。[1], [3]
- ✓user namespace を runc 修正の代替とせず、利用可能な場合は追加の隔離層として使います。[1]