This post is also available in: English (英語)
概要
私は2月にrunCの主要な脆弱性CVE-2019-5736についてこちらのブログを書いたのですが、この脆弱性の背後にある根本的な欠陥はLXCやApache Mesosなどの多くのコンテナランタイムに影響を与えるものでした。影響を受けなかったコンテナランタイムの1つがCoreOS rktで、これについてはコンテナをたんねんに調べていたときにいろんな情報を耳にしました。そこで、「ちょうど時間もあることだし、rktのアーキテクチャをもっと調べて、どこが他とちがうのかを明らかにしてみよう」と考えました。
この結果、rktには3つのまだ指摘されていない脆弱性があることがわかりました。攻撃者がこれらの脆弱性を利用した場合、彼らの制御下にあるポッドにrktユーザーが rkt enter コマンド( docker exec と同等のコマンド)を実行したさい、ホストを侵害することが可能です。本稿執筆時点ではこれらの脆弱性は未修正です。
rkt
rktはオープンソースのコンテナランタイムで、CoreOSによって作成されたCNCFインキュベイティングプロジェクトです。このプロジェクトはコンテナ初期頃はDockerの代用にも使える数少ないオープンソースの1つで、広く親しまれていました。rktの基本的な実行単位はポッドで、ポッドには共有コンテキストで実行する複数のコンテナが含まれています(これはKubernetesと同様です)。
正直に言うと、とくにこれがCNCFプロジェクトだったこともあって、コンテナブレークアウト脆弱性を発見したときにはちょっと興奮してしまいました。ただそのあとすぐ、当該CNCFプロジェクトの開発はもうアクティブには行われていないことに気づきました。コミット、プルリクエスト、イシューはまだあるものの最後のリリースは2018年4月でした。その後少しばかり調査した結果、RedHatが2018年半ばにCoreOSを獲得したことを知りました。また、RedHatは、このプロジェクトの開発を継続するためのリソースを割り振っていないように見えました。RedHatにはCRI-OとpodmanというrunCベースのコンテナランタイムがありますし、これは妥当な話ではあります。rktはオープンソースプロジェクトなので、誰でも開発を引き継げるわけですが、GitHubページの様子だと、実際に参加しているひとはあまりいなさそうです。
開示プロセス
私はこちらの指示に従ってCoreOSとRedHatに脆弱性を報告しました。彼らは問題を認め、脆弱性にCVE IDを割り振りましたが、修正の予定はないと述べました。
「現時点で、Red Hatには報告された問題を一般向けに開示する前に修正するアクティブな計画/タイムラインはありません。このため、私は次のように進めることを提案します。
…
* 筆者が https://github.com/rkt/rkt に2つの問題を公式報告する
* コミュニティでGitHubのIssueトラッカーによって、通常の方法でその後のフォローアップが行われる」
計画に従い、私は発見事項を本日(2019年5月30日)このGitHub Issueで報告しました。これは、理想的な方法ではないかもしれませんが、少なくとも現時点ではコミュニティのすべてのコントリビュータが脆弱性の修正作業を行うことができます。
本番環境でrktを実行しているユーザーが何人いるかは把握していませんが、本稿をお読みの皆さんがrktを実行している場合、 rkt enter コマンドは使わないようにしてください。これには未修正の脆弱性がいくつか含まれるためです。
rkt enter の脆弱性
rkt enter コマンドは docker exec のrkt版です。このコマンドを使用すると実行中のコンテナ内のバイナリを実行できます。コマンドの形式は次のとおりです。
rkt enter [pod-id] [binary-to-run]
rkt enter を使用して実行されるコンテナ内のバイナリにはseccompフィルタリングやcgroup隔離は適用されず、すべての機能が付与されてrootとして実行されます。名前空間によってのみ制限されますが、ブレークアウトやホストの侵害を防ぐには十分ではありません。
RedHatにこれらの問題を報告すると、次の3つのCVE IDが割り振られました。
- CVE-2019-10144:
rkt enter
で実行されるプロセスには、ステージ2ですべての機能が与えられます - CVE-2019-10145:
rkt enter
で実行されるプロセスには、ステージ2でseccompフィルタリングは適用されません - CVE-2019-10147:
rkt enter
で実行されるプロセスは、ステージ2でcgroupsによって制限されません
これらの3つの脆弱性については、このビデオで説明されています。
エクスプロイト
エクスプロイトシナリオは、コンテナへのrootアクセス権を持つ攻撃者と、 rkt enter some-binary を実行してそのコンテナ内のバイナリを実行するrktユーザーで構成されます。攻撃者は、コンテナ内の一般に使用されているバイナリおよびライブラリに悪意のあるコードを挿入し、ユーザーは rkt enter を使用してこのコードを実行します。たとえば、攻撃者は以下を行うことができます。
- コンテナ内の/bin/bashを上書きします。これは、ユーザーが他のコマンドを指定しない場合に、 rkt enter コマンドによって実行されるデフォルトのバイナリです。
- コンテナ内のlibc.so.6を上書きします。これは、 rkt enter で生成されたプロセスによってロードされます。攻撃者はgccコンストラクタ属性を利用できるため、変更されたlibcライブラリがプロセスによってロードされるたびに、攻撃者のコードが実行されます。
rkt enter で生成されたコンテナプロセスのコンテキストで実行すれば、攻撃者はコンテナをエスケープして、ホストに対するrootアクセス権を比較的容易に取得し、seccompフィルタリングなしおよびcgroup隔離なしで、すべての機能が付与された状態で実行することができます。
エクスプロイトの例: ホストのルートディレクトリのマウントによるエスケープ
攻撃者が制御する rkt enter プロセスがホストにアクセスする方法はいくつかありますが、最も単純明快なアプローチはホストのルートディレクトリをマウントする方法です。
攻撃者がrktコンテナを侵害し、コンテナのbashを悪意のあるbashに置き換えて、それにより攻撃者のマシンにリバースシェルが生成される、というシナリオを検討してみましょう。ユーザーが rkt enter ${pod-id} bash を実行すると、悪意のあるbashが実行され、攻撃者は自身のマシンでリバースシェルを受け取ります。このリバースシェルは、 rkt enter によって生成された脆弱なプロセスのコンテキストで実行されるため、ホストから適切に隔離されません。
このため、攻撃者は
mknod および
mount syscallを使用してホストのルートディレクトリをマウントし、ホストに対するrootアクセス権を取得することができます。このシナリオは、以下のビデオでデモされています。
コンテナセキュリティに熱心な皆さんにやや難しい問題を出します - 適切に隔離されたコンテナ環境で実行された場合の(たとえば、 rkt run によって生成されたプロセスまたはDockerコンテナで)、この攻撃の失敗点をリストしてみてください。
コンテナはレイヤードセキュリティの原則に従っているため、攻撃は特定の地点で複数の理由によって失敗する可能性があることを思い出してください。それでは、始めてください。
答えは次のとおりです。
-
mknod
- 過去に、singularityやpodmanなど、いくつかのコンテナランタイムで、コンテナのファイルシステムが nodev フラグでマウントされたために、 mknod syscallは失敗しています。
- rktなど(当然、 rkt enter プロセスは含みません)、いくつかのコンテナランタイムで、mknodを使用したブロックデバイスの作成を拒否するdevice cgroup設定により、 mknod syscallは失敗します。
- LXDのように、デフォルトでユーザー名前空間を使用するコンテナランタイムでは、 mknod syscallは失敗します。詳しくは、こちらの「Effect of capabilities within a user namespace」セクションを参照してください。
- Dockerなどのコンテナランタイムでは、 mknod syscallは実際に機能します!
-
mount
- mount syscallは、 seccomp syscallフィルタリングによって失敗します。
- mount syscallは、機能が不十分なことにより失敗します(CAP_SYS_ADMINがありません)。
- Dockerなどのコンテナランタイムでは、読み込みのためにブロックデバイスを開くことを拒否するdevice cgroup設定により、 mount syscallは失敗します。
結論
rktの調査中に、実行中のホストを侵害する悪意のあるACI/OCIイメージを作成する方法も発見しました。これは確かに理想的ではありませんが、悪意のあるイメージはrktの脅威モジュールには含まれません。rktはイメージの署名をサポートしているため、信頼できないソースからのイメージを実行することは、rktの推奨事項にも適切な使用法にも沿っていません。私はこのことを、このGitHub Issueで可能なsecurity-in-depthの拡張として詳しく報告しました。この問題は、RedHatにも報告しました。
このブログの冒頭で述べたように、もしrktをお使いの場合は、 rkt enter コマンドを使用しないでください。脆弱性にパッチが適用されていないためです。さらに、Docker、podman、LXDなど、より着実に保守されているコンテナランタイムを検討することをお勧めします。
ご質問がありましたら、電子メールまたは@unit42_intelでお気軽にご連絡ください。