This post is also available in: English (英語)
概要
2022年2月4日、Linuxはカーネルにおける新たな特権昇格脆弱性CVE-2022-0492を公表しました。CVE-2022-0492はコンテナの基本構成要素であるLinuxの機能、コントロールグループ(cgroup)における論理バグです。この問題は最近発見されたLinuxの権限昇格脆弱性のなかでもとりわけその単純さできわだつもので、「Linuxカーネルが誤って特権的オペレーションを非特権ユーザーに公開してしまった」という内容になっています。
さいわい、ほとんどのコンテナ環境のデフォルトのセキュリティハードニングはコンテナエスケープ防止には十分なものです。AppArmor、SELinux、Seccompのいずれかを有効にして動作しているコンテナは保護されます。とはいえ、ベストプラクティスのハードニングを行わずにコンテナを実行していたり、特権を追加付与して実行したりしている場合はリスクが生じる可能性があります。「影響の有無を確認するには」のセクションに、脆弱なコンテナ構成の一覧とコンテナ環境が脆弱であるかどうかをテストする方法を提供します。
Unit 42はユーザーの皆さんに修正済みカーネルバージョンへのアップグレードを推奨します。コンテナを実行している場合は、Seccompを有効にし、AppArmorまたはSELinuxが有効になっていることを確認してください。Prisma Cloudをお使いのお客様は「Prisma Cloudによる保護」のセクションでPrisma Cloudの提供する緩和策を確認してください。
CVE-2022-0492は、悪意のあるコンテナがエスケープできるカーネルの脆弱性としてここ数カ月で3つ目の脆弱性となっています。これら3つの脆弱性はいずれも、SeccompとAppArmor/SELinuxのどちらかを有効にしてコンテナを保護することで、コンテナエスケープを防止できます。
2022-03-07 (米国時間) の更新: 「(1) CAP_SYS_ADMINケイパビリティつきで稼働しておりAppArmor、SELinuxのいずれによっても保護されていないコンテナによるエクスプロイト」、「(2) 特権に準ずる権限をもつホストプロセスによるエクスプロイト」に関する言及を削除しました。たしかにこれらはCVE-2022-0492をエクスプロイトして特権を昇格することも可能ですが、これらのシナリオであればとくに脆弱性がなくてもほかに特権昇格する方法があることが多いためです。
本稿で扱う脆弱性 | CVE-2022-0492 |
影響を受ける製品 | Linux |
Unit 42の関連トピック | Container Escape, Cloud |
目次
cgroupの背景
根本原因の解析 - CVE-2022-0492
エクスプロイトの前提条件
多層防御の勝利 - コンテナエスケープの前提条件
影響の有無を確認するには
回避・緩和策
Prisma Cloudによる保護
結論
cgroupの背景
「コントロールグループ(cgroup)」というのはLinuxの機能の1つで、これを使うことで管理者はあるひとまとまりのプロセスのリソース使用を制限・説明・隔離できるようになります。Linuxはv1とv2という2つのcgroupアーキテクチャをサポートしていて、今回の脆弱性はv1のcgroupのみに影響します。なお現在はv1のほうがずっと広く利用されています。また本セクションの残りの部分はこのv1というcgroupのみについて言及します。
cgroupはcgroupfs経由で管理します。cgroupfsというのはファイルシステムとして公開されている管理APIで、通常は/sys/fs/cgroup下にマウントされます。マウントされたcgroupfsにファイルやディレクトリを作成したり書き込んだりすることで、管理者はcgroupを作成したり、cgroupに設定する制限を制御したり、あるcgroupにプロセスを追加したりできます。
cgroupはサブシステムに分かれていて、それぞれのサブシステムが異なるリソースへのアクセスを設定しています。たとえばmemoryサブシステムは、あるひとまとまりのプロセスに対するメモリ使用の上限を設定できます。devicesサブシステムは、そのcgroup内のプロセスからアクセス可能なデバイス(ハードドライブやマウスなど)を定義します。このほかのサブシステムの例としては、blkio (Block IO)、cpu、rdma (Remote Direct Memory Access)などがあります。
各サブシステムは通常、/sys/fs/cgroup/<subsystem>にマウントされ、これがサブシステムのroot cgroupとみなされます。root cgroup下のサブディレクトリが新しい子cgroupを表します。たとえば、Dockerコンテナは通常、/docker/<ctr-id> cgroupの一部になっていて、ホスト上では/sys/fs/cgroup/<subsystem>/docker/<ctr-id>で見つかります。図1はDockerコンテナのcgroupメンバーを示したもので、図2はKubernetesポッドのcgroupメンバーを示しています。
上のスクリーンショットからもわかるとおり、すべてのホストが同じサブシステムをサポートするように構成されているわけではありません。DockerやKubernetesにもさまざまなcgroupの構成があるため、お使いのホストやクラスタ上のコンテナが上の例と同じ内容にはならないことがあります。
根本原因の解析 - CVE-2022-0492
cgroups v1 の特徴の1つにrelease_agentファイルがあります。管理者はこのファイルを使って、cgroup内のプロセスの終了時に実行されるリリースエージェントプログラムを設定できます。この設定は、release_agentファイルに希望するリリースエージェントへのパスを書き込むことで実行します(以下参照)。
$ echo /bin/my-release-agent > /sys/fs/cgroup/memory/release_agent
release_agentファイルは、root cgroupディレクトリ内からのみ見ることができ、すべての子cgroupに影響します。それぞれの子cgroupでは、notify_on_releaseファイルに書き込むことで(子プロセスの1つが終了したときに)リリースエージェントを起動するかしないかを設定できます。次のコマンドは、a_child_cgroupというcgroupに対してnotify_on_release機能を有効にします。
$ echo 1 > /sys/fs/cgroup/memory/a_child_cgroup/notify_on_release
プロセスが終了すると、カーネルはそのcgroupがnotify_on_releaseを有効にしていたかどうかを確認し、有効になっていれば、設定されたrelease_agentバイナリを起動します。リリースエージェントは最も高いパーミッションで実行されます。それはつまり、初期名前空間ですべてのケイパビリティを持つrootプロセスです。ですので、リリースエージェントの設定は、完全なroot権限でどのバイナリを実行するかを決定できることから、特権的操作であるとみなされます。
CVE-2022-0492は検証の欠落に起因しています。単純にLinuxは、release_agentファイルを設定するプロセスに管理者権限(CAP_SYS_ADMINケイパビリティ)があるかどうかをチェックしていなかった、ということです。CVE-2022-0492のパッチ(以下の2-8行目)が非常に短いことがこの脆弱性の単純さをよく表しています。
1 2 3 4 5 6 7 8 |
@@ -549,6 +549,14 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of, + /* + * Release agent gets called with all capabilities, + * require capabilities to set release agent. + */ + if ((of->file->f_cred->user_ns != &init_user_ns) || + !capable(CAP_SYS_ADMIN)) + return -EPERM; |
エクスプロイトの前提条件
先に述べたとおり、release_agentファイルへの書き込みができれば、カーネルに強制的に自分の選んだバイナリを昇格した特権で起動させ、マシン全体を制御できます。では脆弱なマシン上で誰がrelease_agentファイルに書き込めるのでしょうか。カーネルは書き込みプロセスの権限を明示的にはチェックしませんが、通常のファイル所有権とパーミッションの解釈はここでも通用します。
Linuxはrelease_agentファイルの所有者をrootに設定しているので、root(またはCAP_DAC_OVERRIDEケイパビリティでファイルのパーミッションチェックを回避できるプロセス)のみが書き込みを行えます。そのため、この脆弱性ではrootプロセスによる特権の昇格のみが可能です。
一見すると、rootユーザーにしか攻略できない特権昇格の脆弱性は奇異に映るかもしれません。以前であれば、これはセキュリティ上の問題とはみなされなかったでしょう。ですが今日、rootとしての実行は必ずしもマシンの完全な制御は意味しません。rootユーザーと全権限との間には、ケイパビリティ、名前空間、コンテナなどを含むグレーゾーンが存在するのです。「rootプロセスがマシンを完全に制御しているわけではない」というシナリオではCVE-2022-0492は深刻な脆弱性となります。
多層防御の勝利 - コンテナエスケープの前提条件
すべてのコンテナがCVE-2022-0492を悪用してエスケープできるわけではなく、セキュリティプロファイルのゆるいコンテナのみが必要なステップを実行できます。
cgroupfsはコンテナ内では読み取り専用でマウントされるので、コンテナがホストするrelease_agentファイルへの書き込みはできません。CVE-2022-0492を悪用しようとする悪意のあるコンテナは、書き込み可能な別のcgroupfsをマウントする必要があります。
AppArmorとSELinuxはいずれもマウントを防止するので、どちらか1つを有効にして動作しているコンテナは保護されます。どちらも有効でなければ、コンテナはユーザーの名前空間を悪用して cgroupfsをマウントできます。
cgroupfsをマウントするには、現在のcgroup名前空間をホストするユーザー名前空間にCAP_SYS_ADMINケイパビリティが必要です。デフォルトでは、コンテナはCAP_SYS_ADMIMなしで実行されるため、初期ユーザー名前空間にcgroupfsをマウントすることはできません。ただしコンテナはunshare()システムコールを使うことで、CAP_SYS_ADMINケイパビリティを所有しcgroupfsをマウント可能な新たなユーザーとcgroup名前空間を作成できます。
なお、すべてのコンテナが新しいユーザー名前空間を作成できるわけではありません。 基盤となるホストで非特権ユーザー名前空間が有効になっている必要があります。最近のUbuntuのリリースなどではこれがデフォルトになっています。Seccompはunshare()システムコールをブロックするのでSeccompなしで動作しているコンテナのみが新しいユーザー名前空間を作成できます。このスクリーンショットに示したコンテナは、Seccomp、AppArmor、SELinuxを使用せずに実行されています。
上のスクリーンショットでは、コンテナが正常にmemoryサブシステムをマウントしていますが、release_agentファイルがマウントされたディレクトリに含まれていないことにお気づきでしょうか。
先に述べたように、release_agentファイルは、root cgroup からのみ見ることができます。cgroupfsをcgroup名前空間にマウントする際の難点の1つは、それがroot cgroupではなく自身の所属するcgroupをマウントするということです。図1までスクロールして戻ると、コンテナはrootのmemoryサブシステムではなく、子cgroup内(/docker/<id>)で実行されていることがわかります。cgroupマウント内からrelease_agent ファイルが見えるようにするには、コンテナをいずれかのサブシステム内のroot cgroup で実行する必要があります。
再び図1に戻ると、Dockerはコンテナをrootのrdmaサブシステムで実行したことがわかります。rdmaサブシステムに対して同じコマンドを繰り返せばrelease_agentファイルが見えるようになります。
この問題を悪用するには、release_agentファイルに悪意のあるリリースエージェントを書き込む必要があります。上の図6からわかるように、release_agentファイルはrootの所有なのでrootのコンテナプロセスのみがリリースエージェントを設定できます。図7はコンテナがリリースエージェントを設定したときの様子を、図8はrootではないコンテナが設定に失敗したときの様子を示しています。
エスケープの最後のステップは、設定したrelease_agentを呼び出すことで、これは何の権限も必要としません。このステップは常に実行可能で、その環境がCVE-2022-0492に対して脆弱かどうかは関係ないので割愛しました。以下のスクリーンショットから完全なエクスプロイトの様子を確認できます。
このセクションの太字の行を確認するとCVE-2022-0492を悪用してユーザー名前空間を経由でコンテナエスケープするための要件が書かれています。
影響の有無を確認するには
結論から言うと以下を満たす状態で実行されているコンテナはエスケープ可能です。
- rootユーザーとして実行されている。あるいは no_new_privsフラグなしで実行されている。AND
- AppArmor、SELinuxのいずれも有効ではない。AND
- Seccompを有効化していない。AND
- 非特権ユーザー名前空間が有効なホストで実行している。AND
- rootのv1 cgroupで実行されている
Unit 42のリサーチャーは、コンテナ環境がCVE-2022-0492に対して脆弱かどうかをテストできるスクリプトを作成しました。私たちのus-central1-docker.pkg.dev/twistlock-secresearch/public/can-ctr-escape-cve-2022-0492 イメージを実行する新しいコンテナをデプロイするだけで手元環境をテストできます。これはテストスクリプトを実行し、出力を表示した後終了するように設定されています。あるいは、このツールのリポジトリに含まれているDockerfileを使い、ご自身のイメージをビルドして実行することもできます。
なお、可能ではありますが、お手元の既存のコンテナでこのスクリプトを実行することはお勧めしません。それらには私たちのスクリプトが依存しているユーティリティや必要なバージョンが含まれていない場合があり、結果が正しくなくなるかもしれません。
回避・緩和策
CVE-2022-0492 は、最新の Linux リリースで修正されています。すべてのユーザーに、各ディストリビューションの最新カーネルバージョンにアップグレードすることをお勧めします。
アップグレードできない状況下で悪意のあるコンテナからの保護を受けるには、次のいずれかの緩和策を有効にします。
- AppArmorまたはSELinuxを有効にする。詳しくはこちらのKubernetesガイドを参照してください。
- Seccompを有効にする。詳しくはこちらのKubernetesガイドを参照してください。
Prisma Cloudによる保護
Prisma Cloudは脆弱性のあるカーネルバージョンを実行しているホストを検出し、アラートを発報します。また、Seccompで保護されていないコンテナや、AppArmorないしSELinuxのいずれも有効でない状態で実行されているコンテナに対して、コンプライアンスアラートを生成します。
Compute/Defend/Compliance以下で、たとえば特定のコンプライアンスルールに従っていないコンテナや、図13に示すようなSeccompなしで動作しているコンテナをブロックすることでさらに環境のハードニングをはかれます。
結論
CVE-2022-0492はコンテナエスケープに悪用されうるLinuxのあらたな脆弱性です。さいわい、ベストプラクティスに従っている環境はこの脆弱性から保護されています。信頼されていないコンテナや一般公開されているコンテナをホストしている環境のセキュリティ管理が甘いと、当然のことながら、リスクは高くなります。いつもどおり、ホストを修正済みのカーネルバージョンにアップグレードすることが最善の対策です。
この脆弱性を含め、今後のLinuxのゼロデイ脆弱性からの保護には、SeccompとAppArmorないしSELinuxを有効にしてコンテナを実行することを強くお勧めします。Linuxカーネルに存在する多くの特権昇格の脆弱性は、コンテナが新たなユーザー名前空間を作成することを許可されている場合、言い換えれば、コンテナがSeccompなしで実行されている場合にのみ、コンテナエスケープに悪用される可能性があります。
Prisma Cloudをお使いのお客様は、Compute/Monitor/Complianceを確認し、Seccompなしで動作しているコンテナがないかどうかを判断することをお勧めします。図13に示したようにSeccompなしで実行されているコンテナはブロックするようにすることも可能です。
2022-03-08 09:40 JST 英語版更新日 2022-03-07 07:00 PST の内容を反映