Azurescapeの発見: Azure Container Instances(ACI)におけるクロスアカウントでのコンテナ乗っ取り

A conceptual image illustrating vulnerabilities related to containers and Kubernetes, such as Azurescape, the cross-account container takeover in Azure Container Instances discussed here.

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

概要

Azure Container Instances(ACI)は、AzureのContainer-as-a-Service (CaaS)であり、顧客は基盤となるサーバーを管理することなく、Azure上でコンテナを実行することができます。Unit 42のリサーチャーは最近、このACIの重大なセキュリティ問題を発見し、Microsoftに開示しました。悪意のあるAzureユーザーは、これらの問題を悪用して、他のユーザーのコンテナ上でコードを実行したり、顧客の秘密やプラットフォームにデプロイされたイメージを盗んだり、ACIのインフラを仮想通貨(暗号通貨)のマイニングに悪用したりする可能性がありました。リサーチャーたちは、この脆弱性を「Azurescape」と名付けました。これは、パブリッククラウドにおける初のクロスアカウントによるコンテナの乗っ取りです。

Azurescapeでは、悪意のあるユーザーが、ACIをホストするマルチテナントのKubernetesクラスタを侵害し、他のユーザーのコンテナに対する完全な制御を確立することができました。本稿では、調査の過程を説明し、問題の分析結果を提示するとともに、同様の攻撃を防ぐのに役立つ、マルチテナンシーを中心としたKubernetesのセキュリティに関するベストプラクティスを提案します。

Microsoftは、私たちが情報を公開した直後にACIに修正プログラムを適用しました。Unit 42ではAzurescapeが実際に悪用されている様子は確認していません。予防措置として、ACI上でコンテナを実行している場合は、2021年8月31日以前にプラットフォームにデプロイされた特権的な認証情報を失効させ、アクセスログに異常がないか確認することをお勧めします。

Azurescapeの概要については、弊社ブログの「What You Need to Know About Azurescape(Azurescapeについて知っておくべきこと)」をご参照ください。

Azure Container Instancesの背景

Azure Container Instances(ACI)は、2017年7月にリリースされ、大手クラウドプロバイダによる初のContainer-as-a-Service(CaaS)提供となりました。ACIを利用することで、顧客は基盤となるインフラを管理することなく、Azureにコンテナをデプロイすることができます。ACIは、スケーリング、リクエストのルーティング、スケジューリングを行い、コンテナのサーバーレス化を実現します。

AzureのWebサイトでは、ACIについて、「Develop apps fast without managing virtual machines or having to learn new tools – it's just your application, in a container, running in the cloud.(仮想マシンを管理したり、新しいツールを学んだりすることなく、アプリケーションを高速に開発できます。それはただコンテナ内アプリケーションがクラウド上で実行されているにすぎません)」と説明しています。

内部的には、ACIは顧客のコンテナをホストするマルチテナントのクラスタ上に構築されています。もともとはKubernetesクラスタでしたが、この1年でMicrosoftはACIをService Fabricクラスタでもホスティングするようになりました。ここで紹介する問題は、Kubernetes上のACIに影響を与えるものであり、本稿の残りの部分では、そのアーキテクチャについてのみ言及します。数千のコンテナをプラットフォームにデプロイした私たちの検証によると、公開時点でKubernetesはACIで新たに作成されるコンテナの約37%をホストしていました。

この図は、マルチテナントのKubernetesクラスタにホストされたAzure Container Instancesを示しており、マスターノード上のapi-serverが、3つの異なる顧客のために実行されている3つのワーカーノードにどのように関連しているかを示しています。テナントの境界は赤い点線で示されています。
図1 マルチテナント型のKubernetesクラスタ上でホストされるACI

ACIのようなマルチテナント環境ではテナント間の強力な境界を確保する必要があります。ACIでは、その境界はノード仮想マシンです。それぞれの顧客のコンテナは、シングルテナントの専用ノード上にあるKubernetesポッドで実行されます。このKubernetesのマルチテナンシーアプローチは、しばしば「ノード・パー・テナント(1テナント1ノード)」と呼ばれます。

Azurescapeの攻撃シナリオ

ACIは悪意のある隣人から保護されるように作られています。実質的に誰でもコンテナをプラットフォームにデプロイできるため、ACIは悪意のあるコンテナが他の顧客のコンテナを混乱させたり、情報を漏らしたり、コードを実行したり、その他の影響を与えないようにしなければなりません。このような攻撃は、クロスアカウント攻撃やクロステナント攻撃と呼ばれることがあります。

Azure Container Instancesにおけるクロスアカウント攻撃のシナリオを示す図で、悪意のある顧客がワーカーノードを占拠する様子を示しています。
図2 クロスアカウント攻撃のシナリオ

以下のセクションでは、ACIにおけるクロスアカウント攻撃の研究について説明します。この攻撃では、悪意のあるAzureの顧客が自分のコンテナからエスケープし、特権的なKubernetesサービスアカウントトークンを取得してKubernetes api-serverを乗っ取ることで、マルチテナントクラスタとその中で実行されているすべての顧客のコンテナを完全に制御できることがわかりました。

コンテナからのエスケープ

CaaSサービスは、調査が難しいことで有名です。ユーザーに公開されているのは自分のコンテナ環境にのみで、ローカルネットワークへのアクセスはファイアウォールによって無効化されています。そこで私たちは、CaaSプラットフォームがどのようにコンテナを運用しているのかを理解するためにWhoCを作成しました。WhoCは、自身を実行しているコンテナランタイムを読み込むコンテナイメージです。WhoCはあまり議論されることのないLinuxコンテナの設計上の欠陥を突くもので、この欠陥によりLinuxコンテナはホストのコンテナランタイムを読み取ることができます。背後にある考えかたはあの悪名高い CVE-2019-5736とよく似ています。ただしホストのランタイムを上書きするのではなく読み取るというところが違っています。

WhoCをACIにデプロイすることでプラットフォームで使用されているコンテナランタイムを取得できました。意外にも業界標準のコンテナランタイムのrunCが見つかりました。驚いたのは図3に示したバージョンです。

スクリーンショットでは、2016年10月1日にリリースされたrunC v1.0.0-rc2上でAzure Container Instancesが動作しています。
図3 ACIで使用されているコンテナランタイム

RunC v1.0.0-rc2は、2016年10月1日にリリースされたもので、少なくとも2つのコンテナブレイクアウトのCVEに対して脆弱性がありました。私たちは、2019年にこれら脆弱性の1つであるCVE-2019-5736を分析しています。弊社のブログ記事「runCによるDockerコンテナブレークアウト: CVE-2019-5736の解説」でも弊社の分析結果とそれに対するPoC(概念実証)エクスプロイトを紹介しています。

このような古いバージョンのrunCがACIに存在していることを発見したので、当時開発したPoCコンテナイメージに手を入れてACIにデプロイしました。その結果、コンテナからのエスケープに成功し、Kubernetesノードであることが判明した基盤ホスト上でrootとして動作するリバースシェルを手に入れました。

このような古いバージョンのrunCがACIに存在していることを発見したので、当時開発したPoCコンテナイメージに手を入れてACIにデプロイしました。その結果、コンテナからのエスケープに成功し、Kubernetesノードであることが判明した基盤ホスト上でrootとして動作するリバースシェルを手に入れました。ここでは、CVE-2019-5736を悪用して弊社のACIコンテナをエスケープするプロセスのスクリーンショットを示しています。
図4 CVE-2019-5736を悪用して弊社のACIコンテナをエスケープ

コンテナからはエスケープできましたが、それはまだテナント境界であるノードVM内でした。CaaSプラットフォームは、特権の昇格やコンテナのエスケープを可能にするカーネルの脆弱性を持つ高度な攻撃者に耐えるように設計されています。悪意のあるコンテナのブレイクアウトはある程度は想定済みの脅威なのでノードレベルの隔離によって許容されます。

図は、悪意のある顧客からテナントの境界がどのように保護されるかを示しています。コンテナからはエスケープできましたが、それはまだテナント境界であるノードVM内でした。CaaSプラットフォームは、特権の昇格やコンテナのエスケープを可能にするカーネルの脆弱性を持つ高度な攻撃者に耐えるように設計されています。悪意のあるコンテナのブレイクアウトはある程度は想定済みの脅威なのでノードレベルの隔離によって許容されます。
図5 コンテナからはエスケープしたがまだ専用ノードの中

ノード環境の偵察

ノードを見渡すと顧客のコンテナは私たちのコンテナだけでした。Kubeletのの認証情報を使ってクラスタ内のポッドとノードをリストアップしました。このクラスタには約100の顧客用ポッドをホストし、約120のノードがありました。それぞれの顧客には、ポッドを実行するKubernetesのネームスペースが割り当てられていて、私たちのそれはcaas-d98056cf86924d0fad1159XXXXXXXXでした。

そのノードのKubeletが匿名でのアクセスを許可していることがわかったので、近隣のノードのKubeletにアクセスしてみました。隣接するノードへのアクセスを試みたところすべてのリクエストがタイムアウトしました。これはおそらく、ファイアウォールの設定によってワーカーノード間の通信が防止されているためだと思われます。

ノードはkubernetes.azure.com/clusterラベル内にクラスタ名への参照を保持しており、これはcaas-prod<LOCATION>--linux-<ID></LOCATION>のような形式になっていました。

ノードは、kubernetes.azure.com/clusterラベル内にクラスタ名への参照を保持しており、これはスクリーンショットに示すように、CAAS-PROD-<LOCATION>-LINUX-<ID>のような形式になっていました。
図6 クラスタ名

いくつかのブレイクアウトコンテナをデプロイしたところ、それらは異なるKubernetesクラスタに配置されました。各クラスタには、1~125の範囲で固有のクラスタIDが設定されていることがわかりました。これらのクラスタIDは、各地域(たとえば西ヨーロッパ)に数十個のクラスタが存在することを示しています。

Kubernetesのワンデイ

次にクラスタのKubernetesのバージョンを調べました。

Azure Container Instancesは、Kubernetes v1.8.4、v1.9.10、v1.10.9のいずれかを実行しているクラスタにホストされていました。これらのバージョンは2017年11月から2018年10月の間にリリースされたもので、複数の公知の脆弱性が存在します。
図7 ACI Kubernetesのバージョン

ACIは、Kubernetes v1.8.4、v1.9.10、v1.10.9のいずれかを実行しているクラスタにホストされていました。これらのバージョンは2017年11月から2018年10月の間にリリースされたもので、複数の公知の脆弱性が存在します。古いバージョンのKubernetesを実行することは悪い習慣とされていますが、ACI内では必ずしもセキュリティ上の問題を伴うものではありません。悪意のあるノードのコンテキストからの過去の問題が悪用されなければ、セキュリティ上の影響はありません。

私たちはKubernetesの過去の問題を調べ、侵入先のノードで特権を昇格したり、他のノードにアクセスしたりできるような問題を探しました。その結果CVE-2018-1002102という有望な脆弱性を特定しました。

Kubernetesの脆弱性CVE-2018-1002102

api-serverは、Kubeletsに時々通信を行います。たとえばkubectl exec<pod> <cmd>コマンドをサービスする場合、api-server は適切な Kubelet の /execエンドポイントにリクエストを委任します。

CVE-2018-1002102 は、api-server が Kubelets と通信する方法にセキュリティ上の問題があることを示しています。api-serverはリダイレクトを受け入れてしまうのです。api-serverのリクエストを別のノードのKubeletにリダイレクトすることで、悪意のあるKubeletがクラスタ内に拡散します。図8に脆弱性の基本的な流れを示します。

CVE-2018-1002102の基本的な流れは、1) api-serverがサービスするコマンド、2) api-serverが適切なエンドポイントにリクエストを委任、3) 302でリダイレクト、4) クラスタ内での拡散、というものです。
図8 CVE-2018-1002102のフロー

エクスプロイトの前提として必要なものは次のとおりです。

  1. 脆弱性のあるバージョンの api-server:✓
  2. 侵害済みのノード: ✓
  3. api-serverに侵害ノードと通信させる方法。たとえば、侵害されたノード上のポッドにkubectl execを発行することで、これは可能になる: ?

結果的にACIは3つ目の条件も満たしていました。ACIは、アップロードされたコンテナに対するコマンドの実行をkubectl execをミラーするaz container exec コマンドによってサポートしていました。

az container exec --name <my-container> --exec-command <command>

私たちは、CVE-2018-1002102を悪用するカスタムKubeletイメージの作成を進め、受信したexecリクエストを他のノードのポッドにリダイレクトしました。最大限の効果を得るために、api-serverポッドをターゲットとするように設定し、最後にapi-serverコンテナ上でのシェルを確立できるようにaz container exec my-ctr --exec-command /bin/bashを実行しました。このコマンドは失敗しました。

デバッグしてみると、リダイレクト操作は、対象コンテナが同一ノードにホストされている場合にのみ機能することがわかりました。他のノードへの拡散ができなくなるので、これで攻撃を効果的に無効化することができます。CVE-2018-1002102の修正プログラムを調べると、実はこれが脆弱性の修正になっていました。ただ、この時点で腑に落ちないことがありました。対象バージョンのapi-serverにCVE-2018-1002102の脆弱性があることは確認済みだったのに、なぜ修正プログラムが適用済みであるように見えるのかが不思議でした。

そこでノードに届いたexecリクエストを再確認したところ、何が起きているのかが見えてきました。図8に示すように、私たちはリクエストがapi-serverのIPから届くものと想定していました。ですが驚いたことに、これらのリクエストは、デフォルトのネームスペースで稼働している「ブリッジ」と呼ばれるポッドから発信されていました。

このノードに到着したexecリクエストを再確認すると、図のようにデフォルトのネームスペースで動作する「bridge」と呼ばれるポッドからのリクエストであることがわかりました。
図9 az container execセッション中のKubeletへの通信

私たちは、ACIがexecリクエストの処理をapi-serverからカスタムサービスに移していたことを確認しました。これはおそらく、az container execコマンドをapi-serverではなくブリッジポッドにルーティングすることで実装されています。

私たちは、ACIがexecリクエストの処理をapi-serverからカスタムサービスに移していたことを確認しました。これはおそらく、az container execコマンドをapi-serverではなくブリッジポッドにルーティングすることで実装されています。
図10 ブリッジポッドがACIのexecを処理

ブリッジのイメージタグはmaster_20201125.1で、これがCVE-2018-1002102の公開後に更新されたことを示しています。ビルドの日時が最近のものであることやexecリクエストのリダイレクトを拒否していることから判断してCVE-2018-1002102の修正プログラムがブリッジに移植されたものと思われます。Microsoftが自社のカスタムブリッジポッドに脆弱性があることに気づき、しかるべく修正プログラムを適用しておいたことは高く評価されるべきでしょう。いい仕事をしています。

CVE-2018-1002102は、他のケースでも悪用される可能性があります。たとえば、クライアントが悪意のあるKubeletにコンテナのログ(kubectl logsなど)の取得をリクエストした場合などです。これは実際にはACIに関連するもので、この機能はaz container logs コマンドによって実装されています。しかし、execリクエストと同様に、ACIはログ取得の処理を、適切にlog-fetchと名付けられた専用ポッドに委任していました。また、bridgeポッドと同様に、CVE-2018-1002102の修正プログラムはlog-fetchポッドにも移植されており、エクスプロイトを防ぐことができました。

Cluster Adminへの昇格

ということでCVE-2018-1002102は候補から外れてしまいましたが、エクスプロイトのデバッグ中、私たちは奇妙なことに気づきました。ノードに到着したexecリクエストには、図11に示すように、Kubernetesサービスのアカウントトークンを含むAuthorizationヘッダが含まれていました。

ノードに到着したexecリクエストには、図のようにKubernetesサービスのアカウントトークンを含んだAuthorizationヘッダが含まれていました。
図11 ブリッジはサービスアカウントトークンを使ってexecリクエストを送信する

ここでトークンを見つけたのは意外でした。前述したように、クラスタ内のKubeletsは匿名でのアクセスを許可するように設定されていたので、リクエストがトークンで認証される必要はありません。これは昔の実装の名残かもしれません。

Kubernetesのサービスアカウントトークンは、暗号化されていないJSON Web Tokens (JWT)なので解読可能です。下図のように、受信したトークンは、「bridge」サービスアカウントのサービスアカウントトークンです。これは、リクエストがブリッジポッドから発信されていることを考えると納得できます。

受信したトークンは、「bridge」サービスアカウント用のサービスアカウントトークンです。これは、リクエストがブリッジポッドから発信されていることを考えると納得できます。
図12 ブリッジのサービスアカウントトークンを解読

Kubernetesを運用している場合、サービスアカウントトークンを送る相手には注意が必要です。トークンを受け取った人は、その所有者になりすまして自由に使用することができます。トークン泥棒は、盗んだトークンのパーミッションに強い関心を持っていると思われます。api-server は、クライアントが権限を照会するための 2 つの API、SelfSubjectAccessReviewSelfSubjectRulesReviewを公開しています。そして、kubectlは、これらのAPIにアクセスする便利な方法として、kubectl auth can-iを提供しています。

ここに示したのがデフォルトのネームスペースにおける「bridge」トークンの特権です。

ここに示したのがデフォルトのネームスペースにおける「bridge」トークンの特権です。
図13 ブリッジトークンのパーミッション。デフォルトのネームスペース

他のネームスペースを見てみると、パーミッションは一貫しており、(ネームスペースのスコープではなく)クラスタ全体のものであることがわかります。以下は、kube-systemネームスペースでのトークンのパーミッションです。マルチテナントクラスタ内での拡散を可能にするパーミッションがあるか確認してみてください。

これは、pods/exec権限を含むkube-systemネームスペースでのトークンのパーミッションを示しており、トークンを使ってクラスタ内の任意のポッドでコマンドを実行できることを示しています。
図14 ブリッジトークンのパーミッション。kube-system ネームスペース

経験豊富なKubernetesのセキュリティ担当者であればpods/execのパーミッションに気づいたのではないでしょうか。これは、トークンを使ってクラスタ内の任意のポッド(api-serverポッドを含む)でコマンドを実行できることを示しています。図15は、トークンがapi-serverコンテナ上でシェルを開いている様子を示しています。

これは、トークンがapi-serverコンテナ上でシェルを開いている様子を示しています。
図15 ブリッジトークンを使ってapi-server上でシェルを立ち上げる

これにより危険なクロスアカウント攻撃が完了しました。api-server上でコードを実行することで、マルチテナントクラスタとその中のすべての顧客コンテナを完全にコントロールできるクラスタ管理者になれたわけです。

Azurescape攻撃の概要

ここで、悪意のあるAzure顧客がACIをホストするマルチテナントKubernetesクラスタに対する管理者権限の奪取に成功した手順をまとめてみましょう。

  1. CVE-2019-5736をエクスプロイトしたイメージをACIにデプロイ。悪意のあるイメージは実行されるとコンテナをエスケープし基盤ノード上でのコード実行を確立。
  2. そのノード上でKubelet ポート(ポート 10250)のトラフィックを監視しAuthorizationヘッダに JWT トークンを含むリクエストを待ち受ける。
  3. アップロードされたコンテナ上でコマンドを実行するためにaz container execを発行。ブリッジポッドは、侵害されたノード上のKubeletにexecリクエストを送信。
  4. ノード上で、リクエストのAuthorizationヘッダからブリッジトークンを抽出し、それを使ってapi-server上でシェルを立ち上げ。

次のビデオで攻撃を実演しています。

ビデオ 1:悪意のあるコンテナから完全なクラスタ管理者へ

攻撃の影響

悪意のあるAzureユーザーがACIをホストするマルチテナントのKubernetesクラスタを侵害できていた可能性があります。クラスタ管理者である攻撃者は、他のユーザーのコンテナ上でコードを実行したり、顧客の秘密やプラットフォームにデプロイされたイメージを盗んだり、仮想通貨(暗号通貨)マイナーをデプロイしたりできる可能性がありました。洗練された攻撃者であればACIを保護している検出メカニズムをさらに調査し、検出を回避するでしょう。

修正

私たちは責任を持って上記の調査結果をすべてMicrosoftに開示しました。その結果、MicrosoftはACIの修正プログラムをリリースしました。ブリッジポッドがexecリクエストを発行するさい、サービスアカウントトークンがノードに送信されなくなり、報告されたクロステナント攻撃を防げるようになりました。

管理者になるための別の方法: ブリッジSSRF

トークンの問題の報告後、私たちはクラスタ管理者に昇格する方法がほかにもないかを確認したいと考えました。そこで研究を続けたところ、その方法がほかにも見つかりました。この時点で、MicrosoftはKubernetes上で動作するACIコンテナのシェアを減らしていたのでKubernetesクラスタをデフォルトとするリージョンは約10%にとどまっていました。とはいえ、たとえばgitRepoボリュームのように、Kubernetesでしかサポートされていない機能もありました。ACIコンテナがそのような機能を使用している場合は、Kubernetesクラスタ上にデプロイされていました。その他にもコンテナがKubernetes上にデプロイされる可能性が高いケースがあります。たとえばプライベート仮想ネットワーク内のコンテナがそれにあたります。

私たちが2つ目に発見した問題は、ブリッジポッドにサーバーサイドリクエストフォージェリ(SSRF)の脆弱性があることでした。

ブリッジポッドは、az container exec<ctr> <cmd>コマンドをサービスする際に、適切な Kubelet の/execエンドポイントにリクエストを送信します。ブリッジは、Kubeletの/execエンドポイントのAPI仕様に従ってリクエストを組み立てます。この結果が以下のURLになります。

https://<nodeIP>:10250/exec/<customer-namespace>/<customer-pod>/<customer-ctr>?command=<url-encoded-cmd>&error=1&input=1&output=1&tty=1

ブリッジは、<>で囲まれた足りないパラメータをなんとか埋めなければなりません。結果的に<nodeIP>の値は、カスタマーポッドのstatus.hostIPフィールドから取得されていることがわかりました。これはかなり興味深いことに思えました。というのも、ノードは自分のポッドのステータスを更新する権限を持っているからです(たとえば、ポッドのstatus.stateフィールドをRunningTerminatedなどに更新するため)。

そこで私たちは、侵害ノードの認証情報を使ってポッドのstatus.hostIPフィールドを変更してみました。これはうまくいきましたが、1~2秒後、api-serverがhostIPフィールドを元の値に修正しました。変更に持続性はないとはいえ、このフィールドを繰り返し更新してはいけないという法もありません。

そこでポッドのステータスを繰り返し更新する小さなスクリプトを書き、それを使ってstatus.hostIPフィールドを1.1.1.1に設定しました。そののちにaz container execコマンドを発行しました。コマンドが失敗し、ブリッジがexecリクエストを実際のノードIPではなく1.1.1.1に送ったことを確認しました。私たちは、特別に細工されたhostIPがブリッジを騙して他のポッドでコマンドを実行させることができるかどうか考え始めました。

ポッドのstatus.hostIPを別のノードのIPに設定するだけではうまくいきませんでした。Kubeletsは、自分がホストしているコンテナを指しているリクエストのみを受け付けます。ブリッジがexecリクエストを別のKubeletのIPに送ったとしても、URLは私たちのネームスペース、ポッド名、コンテナ名を指しています。

ここで私たちはapi-serverがstatus.hostIPの値が有効なIPであるかどうかを実際には検証しておらず、URL成分を含むあらゆる文字列を受け入れることに気づきました。何度か試行錯誤した結果、ブリッジが私たちのコンテナではなくapi-serverコンテナ上でコマンドを実行するように仕向けるhostIP値を思いつきました。

<apiserver-nodeIP>:10250/exec/kube-system/<apiserver-pod>/<apiserver-container>?command=<url-encoded-command>&error=1&input=1&output=1&tty=1#

このhostIPの値により、ブリッジは以下のURLにexecリクエストを送信します。

https://<apiserver-nodeIP>:10250/exec/kube-system/<apiserver-pod-name>/<apiserver-ctr>?command=<url-encoded-command>&error=1&input=1&output=1&tty=1#:10250/exec/<customer-namespace>/<customer-pod-name>/<customer-ctr-name>?command=<command>&error=1&input=1&output=1&tty=1

サフィックスの#は、URLの残りの部分がURIフラグメントとして扱われ、事実上無視されることを保証します。ポッドのstatus.hostIPにこの値を設定し、az container exec経由でコマンドを発行しました。この攻撃はうまくいきました。私たちのコンテナへのシェルではなくapi-serverコンテナへのシェルが表示されました。その様子は、次のビデオで確認できます。

動画2:ブリッジを騙してapi-serverにシェルを開かせる

これによる影響は先ほどの攻撃とまったく同じで、マルチテナントクラスタを完全に管理下に置くことができます。この問題もMSRCに報告され、ACIに修正プログラムが適用されました。ブリッジは現在はexecリクエストを送信する前にポッドのstatus.hostIPフィールドが有効なIPであることを確認するようになっています。

結論

クロスアカウントの脆弱性は、パブリッククラウドにとって「悪夢」のシナリオだとよく言われます。Azurescapeは、私たちが考えている以上に、それらが現実のものであることを証明しています。クラウドプロバイダは、自社のプラットフォームのセキュリティに多大な投資を行っていますが、未知のゼロデイ脆弱性が存在し、顧客を危険にさらすことは避けられません。クラウドユーザーは、外部からの脅威であろうと、プラットフォーム自体からの脅威であろうと、侵害を抑えこみ、検知するために、クラウドセキュリティに対して多層防御アプローチをとる必要があります。

パブリッククラウドのセキュリティ向上に向けた取り組みの一環として、パロアルトネットワークスでは、高度な脅威モデルや、クラウドプラットフォームとその関連技術の脆弱性テストを含むパブリッククラウドのリサーチに積極的に投資しています。私たちは、こうしたリサーチにより、クロスアカウント攻撃がどのようなものかを明らかし、それが適切な緩和策や検知メカニズムにつながることを願っています。

報告された問題に迅速に修正プログラムを適用し、公開プロセスを適切に処理してくださったMicrosoft MSRCに感謝します。また、報奨金をいただきましてありがとうございました。共同侵入テストやバグバウンティプログラムは、私たち誰もがお世話になっているクラウドサービスの安全性を確保するうえで役に立っています。

Kubernetes環境への類似攻撃の防止

Kubernetesを防御する側の観点からは、複数のベストプラクティス、緩和策、ポリシーが、同様の特徴をもつ攻撃を防止・検出するのに役立ちます。

  • クラスタのインフラを常に最新の状態に保ち、修正プログラムを重要度や状況に応じた優先順位で適用します。
  • 特権をもつサービスアカウントのトークンをapi-server以外に送らないようにします。トークンを受信したアカウントが侵害されると攻撃者はそのトークンの所有者になりすますことができるためです。
  • BoundServiceAccountTokenVolumeを有効化します。この最近正式公開されたフィーチャーゲートは、トークンの有効期限がそのポッドに制限されることを保証してくれます。ポッドが終了するとそのトークンは無効になるのでトークンの窃取による影響を最小限に抑えることができます。
  • ポリシーエンフォーサーを導入し、クラスタ内の疑わしいアクティビティを監視・防止する。ポリシーエンフォーサーで、SelfSubjectAccessReviewまたはSelfSubjectRulesReviewAPIに権限を照会するサービスアカウントやノードには警告を発するように設定します。Prisma Cloudをお使いのお客様は、関連ルールテンプレートをダウンロードしてKubernetes用の組み込みアドミッションコントロール経由でエンフォースできます。ルールを「Alert(警告)」に設定することをお勧めします。このほかにはOPA Gatekeeperのようなオープンソースのツールを利用することもできます。

最後の点について補足しますと、攻撃者はSelfSubjectReviewAPIを積極的に悪用し、盗んだKubernetesの認証情報の権限を検査していることがわかっています。筆者の同僚のリサーチャーであるDaniel Prizmant氏は最近、マルウェア「Siloscape」がこれらのAPIを活用して侵害したノードの権限を取得し、それをもとにクラスタに対するキャンペーンを継続するかどうかを判断している様子を確認しています。この動作をMITREに報告したところ、これがATT&CK for Containersの次のリリースでPermission Group Discovery技術として追加されることになりました。

Kubernetesで安全にマルチテナンシーを実現するのは、クラウドプロバイダにとってもむずかしいものです。Kubernetesにマルチテナンシーを実装するホスティングサービス、クラウドプロバイダ、CI/CDサービスは、プラットフォームを設計するにあたり、以下の点を考慮する必要があります。

  • クラスタの認証情報をテナント境界内で公開しないこと。悪意のあるテナントは、これらの認証情報をさらなる昇格に活用したり、他のテナントやプラットフォーム自体の情報を収集したりする可能性があります。
  • 悪意のあるテナントはコンテナやサンドボックスからエスケープすると想定しておくこと。攻撃者の視点で考えてください。目的は何でしょうか。最初の一手は何でしょうか。それらに合わせた検出メカニズムを導入してください。こうした検知スキームは、敵対的マルチテナント環境において高度で持続的な脅威(APT)やゼロデイ脆弱性に対抗するための必須要件と考えるべきです。
  • たとえノードが侵害されてもそのノードからのラテラルムーブで他のノードが侵害されるようなことがあってはなりません。NodeRestictionアドミッションコントローラを有効にしてノードの権限を最小にします。顧客のコンテナをホストするノード間の通信を防止するため、ファイアウォールルールを設定します。

追加資料