FabricScape: Service Fabricをエスケープしてクラスタを乗っ取る

A conceptual image representing container security, including FabricScape, the container escape vulnerability discussed here.

This post is also available in: English (英語)

概要

Unit 42のリサーチャーは、MicrosoftのService Fabric(Azureで使用されることが多い)に、深刻度が「重要」の脆弱性、「FabricScape (CVE-2022-30137)」が存在することを特定しました。この脆弱性が悪用された場合、Linuxコンテナがノード上で特権昇格してroot権限を奪取し、クラスタ内の全ノードを侵害可能になります。この脆弱性はランタイムアクセスが許可されているコンテナで悪用される可能性がありますが、これはすべてのコンテナにデフォルトで許可されています。

Microsoftによると、Service Fabricは100万以上のアプリケーションをホストし、日次数百万コアを実行しているとのことです。Service Fabricが支えている同社のサービスには、Azure Service FabricAzure SQL DatabaseAzure CosmosDBなど数々のAzure製品のほか、CortanaMicrosoft Power BIなどがあります。

Unit 42で管理しているコンテナで侵害されたワークロードのシミュレーションを行ったところ、Azure Service Fabricの脆弱性悪用に成功しました。Azure Service FabricはAzureのサービスの1つで、クラウド上にプライベートなService Fablicのクラスタをデプロイします。マネージドなマルチテナント型Service Fabricクラスタが提供するAzureサービスに対しても複数回エクスプロイトを試行しましたが、これらは失敗しました。Microsoftがこれらのサービスのコンテナ上でランタイムアクセスを無効化しているためです。

私たちはMicrosoft (Microsoft セキュリティ レスポンス センター、MSRC)と緊密に連携してこの問題の修正に取り組み、2022年6月14日に修正が完了しました。MicrosoftはすでにLinuxクラスタで緩和対策を導入済みだったAzure Service Fabricに対する修正プログラムをリリースし、Service Fabricが支えている製品・サービス群の内部プロダクション環境も更新しました。

自動更新を無効にしてAzure Service Fabricを実行している場合は、Linuxクラスタを最新のService Fabricリリースに更新することをお勧めします。Linuxクラスタを自動更新している場合は対策はとくに必要ありません。

Microsoftとパロアルトネットワークスは、信頼されていないアプリケーションをService Fabricで実行しないことを推奨します。詳しくはService Fabricのドキュメントを参照してください。

この脆弱性を悪用する実際の攻撃はこれまで確認されていませんが、自社環境が脆弱かどうかを確認し、該当する場合はすみやかに修正プログラムを適用することを強くお勧めします。

Unit 42の関連トピック Containers, cloud

目次

Service Fabricの概要
Service Fabricのアーキテクチャ
今回確認された脆弱性
CVE-2022-30137のエクスプロイト
ノード上でのコード実行権限奪取
クラスタ乗っ取り
FabricScapeがもたらす広範な影響
制約
情報開示と緩和策
結論

Service Fabricの概要

Service Fabricはアプリケーションホスティング用プラットフォームです。コンテナだけでなくさまざまな形態のパッケージングやサービス管理をサポートしています。

Microsoftは「Azureのコアサービスの多くがService Fabricを利用している」とした文書を公開しています。

図中のService Fabricを利用したサービスには、Azure Core Infrastructure、Azure Document DB、Intune、Skype for Business、Event Hubs、Bing Cortana、Azure SQL Database、Power BI、IoT Suiteが含まれます。
図1. Service Fabricが支えているサービスの例(2019年時点)

Service Fabricの社内環境での成功を踏まえ、Microsoftは2016年にAzure Service Fabricをプラットフォーム・アズ・ア・サービスとしてリリースしました。これにより、利用者はAzure Cloud上で自社用のService Fabricクラスタを作成・管理できるようになり、これが政府やメディア、自動車ファッション輸送、多国籍コングロマリットなど、さまざまな分野の企業で広く利用されるようになりました。

Service Fabricのアーキテクチャ

FabricScapeの影響をきちんと理解するにはService Fabricの基本アーキテクチャをおおまかに押さえておく必要があります。ですのでまずはService Fabricアーキテクチャの概要をざっとおさらいしておきます。

Kubernetesとおなじく、Service Fabricのクラスタも多数のノードで構成されています。各ノードはコンテナエンジンを稼働し、このコンテナエンジンがコンテナ化された任意のアプリケーションを実行します。Service FabricはLinuxノードとWindowsノードをサポートしており、どちらのノードでもDockerを使っています。なお、WindowsノードではHyper-Vの分離モードをサポートし、最大限の分離を実現しています。Service Fabricクラスタにアプリケーションをデプロイする場合、Service Fabricはアプリケーションマニフェストに従ってアプリケーションをコンテナとしてデプロイします。

舞台裏では、各ノードが複数のコンポーネントを動かしてノード間の協調的動作を可能にすることで、信頼性の高い分散型クラスタをつくりだしています。これらのコンポーネントに関する公開文書はほとんどありませんが、Microsoftが2018年にService Fabricバージョン6.4のソースコードを公開したことで、だれでもコードを読んで各コンポーネントの目的や運用を理解できるようになりました。

Linux Fabric Node。このファブリックにはFabric Gateway、Fabric RM、Fabric DCA、Fabric IS、Fabric CAS、File Store Serviceなどのコンポーネントが含まれている。この図ではそうしたコンポーネントが複数のDockerコンテナの隣に表示されている。
図2. Service FabricのLinuxノードの例

今回確認された脆弱性

Service Fabricはコンテナとしてのアプリケーションのデプロイをサポートしています。各コンテナには、初期化時に新しいログ用ディレクトリが作成され、読み取り・書き込み権限つきでこれがマウントされます。これらのディレクトリはすべて、各ノード上の単一のパスに一元化されています。たとえばAzure Service Fabricの場合、これらのディレクトリは/mnt/sfroot/log/Containersに存在します。

Service Fabricのコンポーネントの1つにDCA(Data Collection Agent)というものがあります。DCAにはさまざまな役割がありますが、その1つが後で処理するために上記ディレクトリからログを収集することです。これらディレクトリへのアクセスには高い権限が求められるので、各ノードはDCAをrootとして実行しています。

同時にDCAは、コンテナによって変更される可能性のあるファイルも処理します。つまり、これらのファイルを処理するDCAの仕組みが脆弱であれば、そこを突けばコンテナをエスケープしてそのノードでのroot権限を奪取できることになります。これはたとえば、ユーザーが悪意のあるコンテナやパッケージを実行した場合や、コンテナが攻撃者に乗っ取られた場合などに発生する可能性があります。

DCAの古いソースコードを調べてみると、関数GetIndex (PersistedIndex.cs:48)には競合状態での任意書き込みの可能性があることに気づきました。

この関数は、ファイルを読み込み、内容が期待される形式であることを確認し、内容の一部を変更し、新しい内容でファイルを上書きするものです。

GetIndex関数は以下の2つのサブファンクションでこうした処理を行っています。

  • LoadFromFile: ファイルを読み込む
  • SaveToFile: 新しいデータをファイルに書き込む
GetIndex関数のコード内容
図3. GetIndex関数

この機能によりsymlinkの競合が発生します。侵害されたコンテナ内にいる攻撃者は、LoadFromFileが読み取るファイルのなかに悪意のあるコンテンツを仕込んでおきます。LoadFromFileがファイルをパースしているあいだに攻撃者はこのファイルを任意のパスへのsymlinkによって上書きします。そうすれば後でSaveToFileがそのsymlinkをたどって悪意のあるコンテンツをそのパスに書き込んでくれます。

DCAはノードファイルシステムのrootとして実行されるので、symlinkをたどってノードのファイルシステム内のファイルを上書きしてしまいます。

CVE-2022-30137のエクスプロイト

この問題を悪用する場合、攻撃者はDCAを起動し、自身が制御下においたファイルに対し、脆弱性のある関数を実行させる必要があります。DCAは先に述べたログのディレクトリに特定のファイル名が作成されるのを監視していて、対象のファイルごとに異なる機能を実行します。この対象の1つにProcessContainerLog.txtというファイルがあります。

図4. コード内のProcessContainerLogの説明文

このファイルの作成を確認したDCAは、ログディレクトリ内の当該パスに対し、最終的にGetIndexを実行することになる関数を何度も実行することになります。これらのパスはコンテナによる変更が可能です。

コードスニペットの開始部分
図5. ProcessContainerLogを監視

つまり悪意のあるコンテナは、自身が制御下においたファイルに対し、GetIndexの実行をトリガーして競合で勝ち、ノードのファイルシステムにある任意のパスの上書きを試みることができます。

つねに競合で勝つために、私たちは悪意のあるファイルのサイズを10MBに変更し、LoadFromFileのパースにかなり時間がかかるようにしました。おかげでsymlinkによる上書き時間をじゅうぶん取れるようになり、毎回競合に勝てるようになりました。

この時点で、コンテナのコンテキストでGetIndexを悪用し、ノード上の任意のファイルを上書きできるようになりました。

この挙動はLinuxコンテナとWindowsコンテナの両方で観察できますが、悪用が可能なのはLinuxコンテナだけです。Windowsコンテナの場合、特権をもたないアクターは対象環境にsymlinkを作成できません。

これ以降は、Linuxノード(Ubuntu 18.04)のエクスプロイトにより、ノード上でコード実行権限を奪取する方法を中心に見ていきます。

ノード上でのコード実行権限奪取

特権つき任意書き込みの脆弱性を利用してマシン上でコード実行権限を得るタスクは非常に容易で、たとえば悪質なsshキーの追加悪意のあるユーザーの追加良性バイナリの上書きによるバックドアのインストールなどさまざまな手法が使えます。

ただし今回はどれも使えません。ノードファイルシステムへの書き込みプリミティブは弱いのです。理由は2つあります。

  1. GetIndexは内部のService Fabricファイルを変更するのでファイル(ペイロードのコンテンツ)が正しい内部フォーマットであることを確認してからSaveToFileに進んでいる
  2. ノードファイルシステム上で上書きされたファイルには実行権限がない
図6. 内部フォーマットの例

調べてみるとこの内部フォーマットは環境変数を含むファイルのフォーマットと非常によく似ていることがわかりました。

FabricScapeに関連する悪意のあるファイルの例
図7 悪意のあるファイルの例

ここでは、エクスプロイトに/etc/environmentを使うことにしました。新しいシェル用の基本的な環境変数が中で指定されているし、他のプログラムからも使用できるからです。すこし調べてみると、Linuxタスクスケジューラ(cron) が実行するすべてのジョブがこのファイルをインポートしており、さらに幸運なことに、すべてのノードにroot権限で毎分実行されるジョブが存在することがわかりました。つまり、「対象ノード上でrootとして実行される新しいプロセスには悪意のある環境変数をインジェクト可能」ということになります。

さらに堀りさげると、スケジューラにジョブがなくてもエクスプロイトが可能であることがわかりました。cronはrootとして内部の毎時ジョブを実行するからです。

この図はcronジョブのスニペットとそれに関連するコメントを示しています。
図8 毎分実行されるcronジョブ

私たちはコード実行権限の奪取にダイナミックリンカハイジャックと呼ばれる手法を使いました。使ったのは環境変数LD_PRELOADです。新しいプロセスの初期化時、リンカは環境変数LD_PRELOADが指す共有オブジェクトをロードします。これを使ってノード上の特権を持つcronジョブに共有オブジェクトをインジェクトします。

私たちはリバースシェルを開始する関数を持つダミーの共有オブジェクトを作成し、この関数にconstructor属性を与えて共有オブジェクトのロード時に自動的にリバースシェルが開始されるようにしました。そして、共有オブジェクトをコンパイルしたものをコンテナ内のログディレクトリにコピーし、LD_PRELOADがこの共有オブジェクトへのパスを指すようにします。

エクスプロイトの1分後、そのノードのrootのコンテキストでのリバースシェル取得に成功しました。

LinuxのService Fabricノードエクスプロイトの流れをステップバイステップで説明したもの。1) 脆弱性を利用してホスト上の/etc/environmentを上書き 2) 新しいcronjobが悪意のある環境変数をインポート、3) LD_PRELOADが悪意のある共有オブジェクトをロード 4) ホストのroot権限のリバースシェル
図9 エクスプロイトの流れ

検証時点で利用可能だった最新バージョン (8.2.1124.1) のAzure Service Fabric サービスを使い、Ubuntu 16.04とUbuntu 18.04の両方でこのエクスプロイトが成功することを確認しました。私たちは検証したすべての回で、競合に勝ち、コンテナをエスケープし、対象ノードでのコード実行権限の奪取に成功しました。

クラスタ乗っ取り

MicrosoftはService Fabricクラスタの管理や操作がしやすいよう、ユーザーにsfctlというCLI ツールを提供しています。

sfctlコマンドの一覧。
図10 sfctlのサブコマンド

クラスタを操作・管理する場合、ユーザーはsfctlに次の2つの引数を与えます。

  1. クラスタのエンドポイントのアドレス
  2. プライベート証明書

コマンドを実行するさい、sfctlはクラスタ内のREST APIにリクエストを送り、認証にこの証明書を使います。このAPIはさまざまな機能を実行可能で、リモートからクラスタを管理できるようにしています。Azure Service Fabricサービスの場合、デフォルトでは19080番ポートでリッスンしており、これはインターネットに公開されているので、ユーザーによるアクセスが可能です。

コードスニペットの開始部分
図11 sfctlでクラスタを選択

こうしたAPIのほかに、このエンドポイントにはブラウザからアクセスできるグラフィカルインターフェースも用意されていて、プライベート証明書が適用されていれば利用できます。このインターフェースはService Fabric Explorerと呼ばれていて、これを使えばクラスタを視覚的に管理・分析できます。

Microsoft Azureが提供するService Fabric Explorerの管理画面のスクリーンショット
図12. Service Fabric Explorerのindexページ

CVE-2022-30137のエクスプロイトでノードのroot権限を奪取してからファイルシステムを探索したところ、ディレクトリ/var/lib/waagent/に機微なファイルが含まれていることが判明しました。見つかったファイルのなかには、クラスタ全体を制御している証明書も含まれていました。

ここで見つけた証明書を適用することで、任意のREST APIエンドポイント(およびロードバランサ)に対する認証に成功し、クラスタ内でさまざまな機能をトリガーするリクエストの送信にも成功しました。

見つけた証明書を使うと、sfctlを実行してクラスタを管理したりService Fabric Explorerをブラウズしたりできました。

この図はFabricScapeを悪用してノードのroot権限を取得後、見つけた証明書をテストするさいに使用されたコードの抜粋。
図13. 見つけた証明書を試しているところ

FabricScapeがもたらす広範な影響

MicrosoftはどのサービスでService Fabricが使われているかを公開はしていません。ただし図1に示した部分的なリストは提供しています。

攻撃者がService Fabric内のコンテナを自身の制御下においた場合、上記に示したとおりクラスタ全体の侵害が可能になります。

制約

Service Fabricクラスタは設計上シングルテナントで、ホストされるアプリケーションは信頼できるものと考えられています。そのためこれらのアプリケーションはデフォルトでService Fabricのランタイムデータにアクセス可能です。このアクセスにより、アプリケーションがService Fabric環境に関するデータを読み取って、特定の場所にログを書き込めるようになります。FabricScapeを悪用するには侵害されたコンテナにランタイムアクセスが必要です。ランタイムアクセスがあることで、ログディレクトリにアクセスできるようになるからです。開発者がアプリケーションを信頼できないと考える場合、またはクラスタがマルチテナントの場合、このアクセスはクラスタ上の各アプリケーションに対して個別に無効化できます。無効化するには各アプリケーションマニフェストを変更し、RemoveServiceFabricRuntimeAccess を「true」に設定します。

Azure Service Fabricでエクスプロイトに成功したので、Azure Container Instances、Azure PostgreSQL、Azure Functionsも試しました。これらのサービスはすべてサーバーレスプランでデプロイでき、マルチテナントのService Fabricクラスタで提供されています。

これらのサービスではFabricScapeのエクスプロイトは無効でした。Azureがこれらのサービスでのランタイムアクセスを無効にしているためです。

情報開示と緩和策

私たちは2022年1月30日にMicrosoft社に対して完動品のエクスプロイトを含む脆弱性を開示しました。

Microsoftは、2022年6月14日にこの問題の修正プログラムを公開しました。

自動更新を無効にしてAzure Service Fabricを実行している場合は、Linuxクラスタを最新のService Fabricリリースに更新することをお勧めします。Linuxクラスタを自動更新している場合は対策はとくに必要ありません。

マネージドなService FabricクラスタをベースとするほかのAzure製品をご利用のお客様はMicrosoftがソフトウェアを更新済みのため安全です。

結論

クラウド移行が急激に進むなか、新たな技術開発により高まる需要に追随すべく、クラウドエコシステムは自己を再発明し続けています。

パロアルトネットワークスは、パブリッククラウドセキュリティ向上の一環として、こうした技術研究に積極的に投資し、問題をベンダに報告することにより、お客様とユーザーの皆さまの安全確保につなげています。