This post is also available in: English (英語)
概要
2021年2月、GoogleはGoogle Kubernetes Engine(GKE)の新しい運用モードであるAutopilotを発表しました。Autopilotでは、GoogleがKubernetesの「ハンズオフ」体験を提供し、ユーザーに代わってクラスターインフラを管理します。このプラットフォームは、リソースの消費量に応じて自動的にノードのプロビジョニングと削除を行い、何もしなくても安全なKubernetesのベストプラクティスをエンフォースしてくれます。
2021年6月にUnit 42のリサーチャーは、GKE Autopilotにおける複数の脆弱性と攻撃手法をGoogleに開示しました。Podを作成できるユーザがこれらを悪用すると、(1)自分のPodをエスケープして基盤ノードを侵害する、(2) 特権を昇格して完全なクラスタ管理者となる、(3)クラスタのオペレータにはまったく見えないバックドアを通じて管理者権限のアクセスをひそかに維持することが可能でした。
たとえば開発者アカウントの侵害でAutopilotクラスタへの足掛かりを得た攻撃者であれば、これらの問題を悪用した特権昇格によって「影の管理者」となり、ひそかにSecretの漏出、マルウェアやクリプトマイナーの配置、ワークロードの妨害を行えた可能性があります。
私たちの情報公開を受けGoogleは報告された問題を修正し、GKE全体にパッチを展開しました。現在はすべてのAutopilotクラスタが保護されています。
本稿は、これらの問題の技術分析と、Kubernetes・GKE環境に対する同様の攻撃防止にむけた緩和策について解説します。問題の概要については、パロアルトネットワークスのブログ「Unit 42 Discloses Newly Discovered Vulnerabilities in GKE Autopilot (Unit 42がGKE Autopilotに新たに見つかっ脆弱性を開示)」を参照してください。
パロアルトネットワークスのお客様は、Prisma CloudのKubernetesアドミッションコントロール機能と監査機能により以下に説明する問題から保護されています。
影響を受ける製品 | Google Kubernetes Engine (GKE) Autopilot |
Unit 42の関連トピック | Container Escape, Cloud |
目次
GKE Autopilotの背景
GKE Autopilot特有の攻撃対象領域
許可されたワークロードになりすましてノードを侵害
ノード侵害による影響
権限無制限の管理者への昇格
攻撃チェーンの全体像: MutatingAdmissionWebhookによる見えないバックドア
全攻撃チェーンによる影響
その他の問題
修正と緩和策
Kubernetes環境における同様の攻撃防止
結論
追加リソース
GKE Autopilotの背景
AutopilotはGKEの新しい運用モードで、Googleが呼ぶところの「ハンズオフ」Kubernetesエクスペリエンスを提供します。GKE Standardではユーザー自身がクラスタインフラを管理し、ノード単位で料金を支払います。GKE Autopilotでは、クラスタのインフラはGoogleが管理し、ユーザーは稼働しているPodに対してのみ料金を支払います。これによりユーザーはアプリケーションに集中し、運用コストを削減できます。
要するにマネージド クラスタ インフラとはGoogleが次の役割を自動で行うことを意味しています。
- Podのリソース消費量に応じてノード数のプロビジョニングや調整を行う
- クラスタが安全なベストプラクティスを遵守し、Google が安全に管理できるようにするためのビルトインポリシーをエンフォースする
以下は、Autopilotのアーキテクチャを簡略化した図です。AutoPilot固有のコンポーネントを緑色で表示し、先の(1)と(2)の役割に応じて番号を振っています。GKE StandardではノードがCompute EngineのVMとして見えるのに対し、Autopilotではノードが完全にGoogleによって管理されているので緑色で表示してあります。
図1にあるように2つのコンポーネントがAutopilotのポリシーをエンフォースします。1つめは、Kubernetesのポリシーエンフォースに広く利用されているオープンソースプロジェクトの「OPA Gatekeeper」で、これがAdmission Webhookを検証します。2つめはGoogleがKubernetesのソースコードを改変して実装したGKEAutopilotという名前のプロプラエタリなKubernetes認証モードです。
このビルトインポリシーには (a) Googleが管理するクラスタコンポーネント(ノードなど)にユーザーがアクセスするのを防ぐ、(b) 安全なKubernetesのベストプラクティスを維持する、という2つの目的があります。たとえばAutopilotは特権コンテナの実行を禁止しており、これによって(a)と(b)の両方を実現しています。
Autopilotのポリシーはコンテナエスケープの防止にはとどまりません。図3、図4、図5では、いくつかの興味深い事例を紹介しています。ポリシーによってエンフォースされるすべての制限はGKEのドキュメントに記載されています。
上図のエラーメッセージを読むと、図2と図5はGatekeeperが、図3と図4はGKEAutopilotの認証モードが操作を防止していることがわかります。
GKE Autopilot特有の攻撃対象領域
Autopilotのビルトインポリシーのおかげでいくつかのエクスプロイト方法は何もしなくてもブロックされるので、標準のKubernetesやGKE Standardと比べたセキュリティ態勢はよくなります。とはいえ、そこにはAutopilotならではの攻撃対象領域も生まれます。
- 管理者がリスクの高い設定の防止についてAutopilotポリシーに依存してしまう。攻撃者が何らかの方法でこのポリシーを回避できれば、たとえば特権付きコンテナのデプロイなど、ユーザーがブロックを前提としている方法で特権を昇格されてしまう。
- Autopilotの管理者は完全な特権はもっておらず、ビルトインポリシーによってノードや特定の特権付きKubernetes APIへのアクセスが制限されている。攻撃者がAutopilotのポリシーを回避できれば、管理者より高い権限を得られ、見えないバックドアを開かせてしまう可能性がある。
以下では、これらの攻撃対象領域に該当する脆弱性、権限昇格テクニック、および確認した永続化手法を紹介します。これらをつなぎ合わせることで、Podを作成できる制限されたユーザーが、(1)ノードを侵害すること、(2)特権昇格して権限無制限のクラスタ管理者になること、(3)クラスタに不可視で永続的なバックドアを設置すること、が可能になります。
許可されたワークロードになりすましてノードを侵害
私たちの調査は、Autopilotのドキュメントにある次のパラグラフから始まりました。
「Google の目的は、ノード仮想マシンの意図しないアクセスを防ぐことです。Google では、脆弱性報奨金プログラム(VRP)でセキュリティ効果に関する報告を受け付けています」
これは面白いチャレンジだと思い、Autopilotのクラスタを作って、いろいろと調べてみました。AutopilotはOPA Gatekeeperをクラスタにインストールし、特権コンテナなどの高リスク構成を防止するポリシー(Gatekeeper用語では「制約(constraints)」と呼ばれる)を複数設定します。このクラスタには、allowlistedworkloadsという名前の興味深いCRD(カスタムリソース定義)もありました。
図2に示したように、Autopilotはコンテナエスケープを許すようなPodの構成を禁止しています。ある程度のノードアクセスを必要とするアドオンをサポートするため、Autopilotは許可リスト化されたワークロードという概念を作りました。あるコンテナが許可リスト化されたワークロードにマッチする場合、allowlistedworkloadの設定で指定された特権機能の使用が許可されます。6月の時点でこの許可リストに載っているワークロードはDatadogのエージェントだけでした。
以下は、私たちが注目したDatadogエージェントの1つに対する許可リスト化されたワークロードの設定です。あるコンテナがリストにあるコマンドやイメージを指定した場合、リストにあるホストのパスを読み取り専用ボリュームにマウントすることが許可されます。
ここで問題になるのは、検証が十分行われていないことです。ただコマンドとイメージをチェックしただけでは、コンテナがDatadogコードを実行していると確認するのに不十分です。以下のPodSpecを使えば、コンテナはDatadogエージェントになりすまして攻撃者のコントロールするコードを実行し、無防備なホストのボリュームを悪用してエスケープ可能です。
以下のビデオでは、悪意のあるユーザーが、Datadogエージェントを装ったPodをデプロイしています。このPodは以下のステップを実行して基盤ノードを乗っ取ります。
- マウントされたcontainerdソケットを悪用してホストファイルシステムをマウントする特権付きコンテナを作成する
- 特権を持つコンテナにsystemdサービスをインストールさせ、systemdサービスにノードから攻撃者がコントロールするマシンへのリバースシェルを生成させる
ビデオ1 allowlistedworkloadのワークロードになりすまして基盤ノードを侵害
ノード侵害による影響
Podを作成できる攻撃者は、この問題を悪用して、悪意のあるコンテナを作成し、基盤ノードをエスケープして乗っ取る可能性があります。Autopilotユーザーはプラットフォーム側でこの手の攻撃は防ぐものと想定していることから、不意打ちを食らってしまいます。
ノード侵害で以下のような攻撃ベクトルが生まれます。
- 攻撃者は隣接するPodやそのサービス アカウントトークンをただちに制御可能になることから特権昇格や他の名前空間に侵害を広げるおそれがあります。
- 攻撃者はそのノードのインスタンス メタデータ エンドポイントに対しアクセストークンについてのクエリを実行できます。デフォルトでは、このトークンはユーザー プロジェクト内のクラウドストレージへの読み取りアクセスを提供します。
- Autopilot管理者はノードにアクセスできないので、攻撃者がこの問題を悪用し、ノードに秘密のマルウェアやクリプトマイナーをインストールするおそれがあります。ただしAutopilotはノードのスケールを自動的に行うのでマルウェアを確実に永続化させるのは容易ではありません。
- 攻撃者は、基盤Kubeletの認証情報(クレデンシャル)にアクセスできるようになり、ほぼすべてのクラスタオブジェクトを見られるようになります。
最後に、Autopilotは実行されているPod単位で課金するので、ずる賢いユーザーであればこの問題を悪用して一部のワークロードをノード上で直接実行させ、コストを削減することもありえたでしょう。ただし私たちは不正な方法を使わずに料金を削減することをお勧めします。
権限無制限の管理者への昇格
私たちは、決意の固い侵入者であれば取るであろう道筋をたどり、このコンテナエスケープをクラスタ全体の乗っ取りに拡大する確実な方法を探しました。ノードを侵害することで、攻撃者は近隣のPodのサービス アカウント トークンを盗み出すことができます。ということは、強力なサービスアカウントを持つPodをホストしているノードをターゲットにするのが理にかなっています。ターゲットにするのはユーザーのデプロイしたPodでもよいし、より興味深くしたければ全AutopilotクラスタにネイティブにデプロイされているシステムPodでもよいでしょう。
ビルトインポリシーを調べた結果、私たちはAutopilotがkube-systemのサービスアカウントを完全に除外していることに気づきました。その意味で、kube-system Podが最も興味深いターゲットとなりました。ポリシーを気にすることなく盗んだトークンを自由に使えるからです。
Autopilot内にある強力なPodを探して、私たちはsa-hunterというPodのサービスアカウントとKubernetesの権限(rolesやclusterrolesなど)の対応付けを行うPythonツールを用意しました。既存のツールでは、サービスアカウントとそのパーミッションを関連付けますが、実際に特定のサービスアカウントを使っているPodがあるかどうかは表示されません。図11にsa-hunter出力例を示します。
sa-hunterはデフォルトでインストールされている2つの強力なkube-system Pod、stackdriver-metadata-agent-cluster-levelとmetrics-serverを発見しました。図12に示すように、どちらのPodも既存のデプロイメントを更新できます。この特権は一見何の問題もないように見えますが、クラスタの完全な管理者に昇格するのに十分なものです。興味深いことに、これらのPodはGKE Standardでもデフォルトでデプロイされているので以下の特権昇格テクニックは StandardとAutopilotのすべてのGKEクラスタで有効になっています。
stackdriver-metadata-agent-cluster-levelまたはmetrics-server Podをホストするノードを乗っ取った後、攻撃者はノードのファイルシステムからサービス アカウント トークンを収穫できます。このトークンを手に入れた攻撃者は、次の3つの簡単なステップでクラスタのあらゆるサービスアカウントの権限を奪取できます。
- 既存のデプロイメントのサービス アカウントをターゲットのサービス アカウントに更新する。プリインストールされたデプロイメントがいくつかあるので、そのうちのどれかをこのステップで使用すればよい
- そのデプロイメントに悪意のあるコンテナを追加する
- その悪意のあるコンテナを使い、コンテナ内で/run/secrets/kubernetes.io/serviceaccount/tokenにマウントされているターゲットのサービス アカウント トークンを取得する
これを意味のある権限昇格にするには、攻撃者は強力なサービスアカウントをターゲットにする必要があります。kube-systemの名前空間にはプリインストールされた非常に強力なサービスアカウントが多数あるのでその中からどれかを選べばよいでしょう。既存のクラスタロールに任意の権限を追加できるclusterrole-aggregation-controller (CRAC)サービスアカウントが有力候補と思われます。
図13に示した手法でCRACトークンを取得後、攻撃者はCRACにバインドされたクラスタロールを更新してすべての権限を獲得できます。この時点で、攻撃者は事実上クラスタ管理者となりAutopilotのポリシーからも除外されます(図10参照)。
話を戻すと、攻撃者がこの特権昇格テクニックを前述のコンテナエスケープと連動させたい場合、stackdriver-metadata-agent-clusterレベルまたはmetrics-server Podをホストするノード上で自分のブレイクアウト用Podを何らかの方法でスケジューリングする必要があります。AutopilotはnodeSelectorを持つPodを拒否しますが、最も単純な形式のノードの割り当てである nodeNameフィールドは許可されています。
ターゲットノードに別Podに割くリソースが十分あれば、nodeNameフィールドを使ってブレイクアウトPodをターゲット ノードに確実に着地させることができます。たとえターゲット ノード上に余裕がなくてもまだ攻撃者がとれる方法はいくつかあります。(1)ターゲットノードを監視してPodが削除されるのを待つ。または、(2) Podを作成してノードのスケールアップを誘発し、Autopilotのオートスケーラを騙してワークロードを再分配させることで、強力なPodがリソースに余裕のあるノードに置かれるようにする。などです。
攻撃チェーンの全体像: MutatingAdmissionWebhookによる見えないバックドア
ビデオ2はコンテナエスケープとGKEに組み込まれた特権昇格ルートを組み合わせた攻撃チェーンの全体像を示したものです。エクスプロイト後はビルトインポリシーから除外されるため、攻撃者はAutopilotの管理者よりも高い権限を持つことになります(図10参照)。このレベルのアクセス権を悪用すればMutatingAdmissionWebhookの形で見えない永続的バックドアをインストール できます。
MutatingAdmissionWebhookは、Pod や Secret を含む、クラスタで作成または更新されたすべてのオブジェクトを受信します。恐ろしいことに、Webhookは受信したオブジェクトを任意に変更(Mutation)できるため、これはとてつもなく強力なバックドアとなります。図4で示したように、Autopilotの管理者はMutatingAdmissionWebhookをリストできないので、このバックドアは見えません。
ビデオ 2 Pod作成、権限無制限の管理者、そして見えないバックドアへ
全攻撃チェーンによる影響
修正前であれば、攻撃者は本稿で述べた問題を悪用し、任意のAutopilotクラスタ上の限定的な侵害を完全なクラスタ乗っ取りに変えることができました。ユーザーには見えないWebhookを利用することで、攻撃者は管理者権限を密かに永続化し、事実上「影の管理者」になることができます。そうなればひそかにSecretの漏出、マルウェアやクリプトマイナーの配置、ワークロードの妨害を行えた可能性があります。
その他の問題
私たちの調査では、ノードの侵害を可能にする、より影響の小さい2つの問題を発見しました。1つめの問題は、Autopilotのポリシーから除外されたデフォルトの名前空間にある2つのサービスアカウント名、csi-attacherとotelsvcにかかわるものです。攻撃者がデフォルトの名前空間のコントロールを獲得すれば、ビルトインポリシーを回避してこれらのサービスアカウントを作成することができたでしょう。そうすれば攻撃者は特権付きのPodを作成してノードを侵害し、ここで説明した特権昇格テクニックを使ってクラスタ全体を乗っ取ることができます。
2つめの問題はLoad Balancerサービスを通じてCVE-2020-8554を悪用し、ノードを侵害するというものでしたが、これを悪用するには管理者権限が必要でした。ここでの攻撃シナリオは、すでにAutopilotクラスタを侵害済みの攻撃者が、ビルトインポリシーを回避して秘密のバックドアを確立しようとするものです。
修正と緩和策
Googleは私たちの勧告にしたがい、ここ数ヶ月のあいだにGKE Autopilotに多数の修正と緩和策を導入しました。これらの施策はここで報告した攻撃を防ぎ、同様の悪用に対してプラットフォームを強化するものです。
- クラスタ管理者はMutatingAdmissionWebhookの一覧表示や閲覧、作成までを実行できるようになりましたが、見えないバックドアとしての悪用は防げるようになりました。
- Googleはallowlistedworkload検証プロセスを強化しました。
- ポリシーのエンフォースがOPA GatekeeperからGoogleのポリシーコントローラーに移動し、ユーザーが独自のGatekeeperインスタンスを導入できるようになりました。これでユーザーはビルトインポリシーに加えて独自のポリシーをエンフォースできます。多層防御としてまた将来起こりうる問題を緩和するため、GKE Standardに対してであれば導入するであろうポリシーと同じポリシーを導入されることをお勧めします。
- ビルトインポリシーは表示されなくなりました。
- csi-attacher、otelsvcのサービスアカウントはAutopilotポリシーから除外されなくなりました。
- Googleは、攻撃で悪用された強力なkube-system Podを制限するOPA Gatekeeperポリシーをオープンソース化しました。このポリシーはこれらのPodが既存のPodに新しいサービスアカウントを割り当てることを防止します。詳しくは、GKEのハードニングガイドを確認してください。
Googleの公式アドバイザリにはGoogleから見た問題点の解説とその緩和策が掲載されていますのでぜひご一読ください。
Kubernetes環境への類似攻撃の防止
今回の攻撃はKubernetesの特権昇格に分類されうるもので、悪用された場合、限られたアクセスしかない攻撃者がクラスタ上でより広い権限を獲得できるようになります。この手の攻撃者による後続のアクティビティは、クラスタのサプライチェーンにある悪意のあるイメージや、一般に公開された脆弱なサービス、窃取された認証情報(クレデンシャル)、あるいはインサイダーによる脅威などの最初の侵害から始まる必要があります。お使いのクラスタのソフトウェアサプライチェーンやアイデンティティ、外部境界を保護することで、クラスタ内でこのような侵害が発生する可能性を減らせます。
それでも巧妙な攻撃者はクラスタに侵入するための創造的な方法を見つけるかもしれません。認証されていないアクセスを許可するKubeletのような、ありがちな設定ミスを積極的に解決していくことで、侵入者が利用できる内部にある攻撃対象領域を大幅に減らせます。NetworkPoliciesやPodSecurityStandardsなどのセキュリティ コントロールがあれば、アクターをさらに制限し、士気を低下させることができます。
今回紹介した攻撃チェーンの場合、攻撃者が1ノードを侵害するだけでクラスタ全体の乗っ取りに成功しました。根本的な問題はノードの権限ではなく、そのノードがホストしている強力なPodの権限にあり、その中にはデプロイメントを更新する権限も含まれていました。この権限は一見なんらかの制限を受けているように見えて、じつはクラスタ管理者と同等の権限を持っています。
強力なPodは本番クラスタでもよくみられます。それらは基盤Kubernetesプラットフォームによりネイティブにインストールされたり、人気のあるオープンソースのアドオンに導入されたりしています。強力なPodをホストするノードが侵害されると、攻撃者は簡単にPodの強力なサービス アカウント トークンを取得し、クラスタ内で侵害を広げられます。
強力なPodに対処するのはやっかいです。その主な理由は正当な理由でそのパーミッションが必要とされていることがあるからです。最初のステップは検出です。クラスタに強力なPodが存在するかどうかを特定します。sa-hunterがその一助となれば幸いです。なお、強力なPodの自動検出に焦点を当てた追加ツールをリリースする予定です。
クラスタ内で強力なPodが特定された場合、以下のいずれかの方法を取ることをお勧めします。
- 強力なPodを管理する場合は、そのサービスアカウントから不要な権限を削除したり、特定の名前空間やリソース名にスコープを限定することが可能かどうか検討してください。
- これらのPodが外部ソリューションの一部である場合、関連するクラウドプロバイダまたはプロジェクトに連絡してPodの権限を削減するよう求めてください。PodがマネージドKubernetesサービスでデプロイされている場合、そのサービスはコントロール プレーン コントローラで置き換え可能な場合があります。
- Kubernetesのパーミッションには寛容すぎるものがあり、対象となるPodがそのパーミッションによって露出する高リスクな操作へのアクセスを必要としない場合があります。その場合はPodが特定の高リスクな操作を実行できないようにするか、よりよい対策として、許可・期待される特定操作セットにPodを制限するポリシーを(たとえばOPA Gatekeeperを介して)実装することが可能かもしれません。
- Taints、NodeAffinity、またはPodAntiAffinityルールを使用し、信頼できないPodや一般公開されているPodから強力なPodを分離し、それらが同じノードで実行されないようにします。
3つ目のアプローチ例として、以下のRegoポリシーで今回紹介した特権昇格攻撃を阻止できます。この攻撃は、デプロイメントを更新できるシステムPodを悪用し、既存のデプロイメントのサービスアカウントを強力なものに置き換えるものです。この強力なPodのソースコードを調べてみると、更新対象のデプロイメントのサービスアカウントを変更する機能をもっていなくてもよいことがわかりました。以下のポリシーはこのことを利用して、これらのPodがデプロイメントのサービスアカウントを予期せず更新することを禁じています。GKE上のPrisma Cloudユーザーは、このポリシーをAlert上でアドミッション ルール セットとしてインポートすることが推奨されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
match[{"msg": msg}] { input.request.object.kind == "Deployment" request_by_powerful_dep_update_sa(input.request.userInfo.username) old_spec := input.request.oldObject.spec.template.spec new_spec := input.request.object.spec.template.spec new_service_account := is_updating_the_service_account(old_spec, new_spec) msg := sprintf("SA '%v' may be compromised, it unexpectedly tried to replace the serviceaccount of 'deployment/%v:%v' to '%v'", [input.request.userInfo.username, input.request.object.metadata.namespace, input.request.object.metadata.name, new_service_account]) } request_by_powerful_dep_update_sa(username) { # metrics-server pod on GKE username == "system:serviceaccount:kube-system:metrics-server" } { # stackdriver pod on older GKE clusters username == "system:serviceaccount:kube-system:metadata-agent" } is_updating_the_service_account(oldspec, newspec) = new_service_account { oldspec.serviceAccountName != newspec.serviceAccountName new_service_account := newspec.serviceAccountName } { not has_key(oldspec, "serviceAccountName") new_service_account := newspec.serviceAccountName } has_key(obj, k) { _ = obj[k] } |
結論
組織がKubernetesに移行すれば攻撃者もそれに追随してきます。Silocapeのような最近のマルウェアサンプルは、攻撃者が単純なテクニックを使う攻撃からとくにKubernetesに向けた高度な攻撃へと進化していることを示しています。巧妙な攻撃者に対しては、クラスタ境界を保護するだけでは不十分な場合があります。私たちは、攻撃者の「第2ステージ」となるフォローアップアクティビティを検知・防止できるようなポリシーや監査エンジンを防御側が採用することを推奨しています。本稿での調査により、そうしたアクティビティがどのようなものかを明らかできていれば幸いです。Prisma Cloudをご利用のお客様には、この脅威への対処を目的とした弊社のKubernetesのアドミッションコントロール機能と監査機能を有効化することをお勧めします。
これらの問題の解決にご協力いただいたGoogleの皆様と、報奨金、そしてチャリティに寄付された場合に報奨金が2倍になるというすばらしいポリシーに感謝いたします。
パロアルトネットワークスはファイルサンプルや侵害の兆候などをふくむこれらの調査結果をCyber Threat Alliance (CTA サイバー脅威アライアンス) のメンバーと共有しました。CTA のメンバーはこのインテリジェンスを使用して、お客様に保護を迅速に提供し、悪意のあるサイバー攻撃者を体系的に阻害することができます。詳細については Cyber Threat Alliance からご覧ください。