クラスターに潜むリスク:Kubernetes におけるローカルボリュームの脆弱性
編集・協力:Tricia Howard
エグゼクティブサマリー
先日、Akamai のセキュリティ研究者である Tomer Peled が 重大度の高い脆弱性 を Kubernetes 内に発見しました。 CVE-2023-5528 と指定され、7.2 の CVSS スコアが付けられました。
この脆弱性により、 SYSTEM 権限があれば、Kubernetes クラスター内のすべての Windows エンドポイントでリモートコードを実行することができます。この脆弱性を不正利用するためには、攻撃者はクラスターに悪性の YAML ファイルを適用する必要があります。
この脆弱性により、クラスター内のすべての Windows ノードで完全な乗っ取りが成立する可能性があります。
この脆弱性は、Kubernetes のデフォルトインストール(バージョン 1.28.4 よりも前のバージョン)で悪用される可能性があり、オンプレミスデプロイメントと Azure Kubernetes Service の両方に対してテストされました。
- このブログ投稿では、概念実証用の YAML ファイルのほかに、この脆弱性をブロックするための Open Policy Agent(OPA) ルールを提供しています。
はじめに
Kubernetes や コンテナ は全般的に見て、セキュリティの世界では特に注目を浴びており、(私たちを含む)世界中の研究者にとって格好の研究材料となっています。研究を始めて最初に関心を抱いたのは、 CVE-2023-3676 (CVSS スコア 8.8)です。これは、コマンドインジェクションの脆弱性の 1 つで、悪性の YAML ファイルをクラスターに適用することで悪用される可能性があります。Kubernetes フレームワークでは、Container Network Interface の構成からポッド管理、さらにはシークレット処理まで、基本的にすべてに YAML ファイルが使用されているため、この脆弱性が悪用されると非常に危険な状態に陥る可能性があります。
この脆弱性が発見されたことで、同じ根本原因に根ざす他の 2 つの脆弱性が発見されました。それは、安全でない関数呼び出しと、ユーザー入力のサニタイズの欠落です。
ボリュームを含むポッドを作成する、YAML ファイルの subPath パラメーターをサニタイズしていない場合、悪性のインジェクションの攻撃を受ける可能性があります。これは当初に得た所見でしたが、その調査の最後の方では、コードの中に、別のコマンドインジェクションの脆弱性につながりそうな場所があることがわかりました。何度か試した結果、「kubelet」サービス(SYSTEM 権限)のコマンドを実行するときと同様の結果を得ることができました。今回は、 CVE-2023-5528が研究対象です。
このブログ投稿では、脆弱性の詳細と、それを可能にする Kubernetes ソースコードの問題について説明し、Kubernetes チームのパッチとその効果を分析します。 パッチをできるだけ早く適用することをお勧めしますが、影響を受けるノードを探す方法や、このタイプのふるまいを検知しブロックするうえで役に立つ Open Policy Agent(OPA)ルールを適用する方法について簡単に説明します。
Kubernetes 自体とそのサイドカープロジェクト(たとえば ingressなど)の複数のコード領域で入力のサニタイズが欠けているため、この投稿では、Kubernetes 構成の YAML を検証することがいかに重要かについて改めて警鐘を鳴らしています。
脆弱性の詳細
この脆弱性自体の詳細説明に入る前に、まず、 Kubernetes内の主要コンポーネントを理解する必要があります。
Kubernetes ボリュームとは
Kubernetes ボリューム は、ポッド間でのデータ共有をサポートしたり、ポッドの存続期間の終了後にデータを永続的に保存したりするための機能です。開発者が使用できるボリュームのタイプはさまざまです。たとえば、CVE-2023-3676 に関するかつての調査では、 hostPath ボリュームを使用していました。今回の脆弱性の対象として、 ローカルボリュームという、Kubernetes 内のもう一つのボリュームタイプに焦点を当てています。ローカルボリュームは、ユーザーがディスクパーティションをポッド内にマウントできるように設計されていますが、hostPath ボリュームは、ユーザーが自分のノード(ホスト)からポッドにディレクトリをマウントできるように設計されています。
ローカルボリュームを含むポッドを作成する際に kubelet サービスは(最終的に)「MountSensitive()」関数に到達します。その中には、ノード上のボリュームの場所とポッド内の場所との間にシンボリックリンクを作成する「exec.command」へのコマンドライン呼び出しがあります(図 1)。
使いやすいという理由で、多くの端末でコマンドを連結したバージョン(図 2)が使用されます。これは、Windows の コマンドプロンプト (cmd) の例です。トークン「&&」を使用すると、端末は 2 つ以上のコマンドを順次実行します。
C:\Users\user>echo "by using &&" && echo "we can execute multiple commands in the same command line"
"by using &&"
"we can execute multiple commands in the same command line"
C:\Users\user>
図 2:コマンド連結: cmd
cmd でパラメーターを 1 つでも制御できるということは、コマンドインジェクションを使用できるということです。ただし、これにはいくつかの前提条件があります。ユーザーがローカルボリュームを使用するためには、 persistentVolumeを指定するか、または作成する必要があります。
persistentVolume とは
persistentVolume は、クラスター管理者がストレージスペースをあらかじめプロビジョニングするために作成できるストレージリソースであり、ポッドの存続期間よりも長く存在します(図 3)。 persistentVolume が作成されると、ユーザーは persistentVolumeClaimを使用してストレージスペースを要求できます。
この場所が、インジェクションの配置場所となります。 攻撃者は、 persistentVolume YAML ファイル内の「local.path」パラメーターの値を変更して、マウント処理中に実行される悪性のコマンドを追加できるようにします。
図 4 では、良性の「&calc.exe&&」(ノード上で電卓ツールを開く)を使用していますが、このプロセスははるかに悪性の結果を得るために悪用できます。
図 5 は、「悪性」の電卓コマンドを挿入した後の、ターゲットノードでの成功した不正利用がどのようなものなのかを示しています。
パッチ分析
インジェクションを受ける可能性を排除するために、Kubernetes チームは cmd 呼び出しを削除し、次と同じ処理を実行するネイティブの GO 関数「os.Symlink()」に置き換えました(図 6)。
現在、GO「os」ライブラリは、当初の意図どおり、シンボリックリンク処理のみを実行するようになっています。
自分は脆弱か?
この脆弱性の影響を受けるユーザーは、Kubernetes のバージョンが 1.28.4 より前のユーザーになると考えられます。まだパッチを適用していない場合は、そのパッチに優先的に適用することをお勧めします。これは、クラスター内に Windows ノードがある組織に特に当てはまります。そのような組織は、脆弱性を抱え、危険な状態にあります。
しかし幸いなことに、これは業界標準ではないようです。管理者は、クラスターコントローラーで、図 7 に示すコマンドを実行することで、組織のクラスターに Windows ノードが含まれているかどうかを簡単にテストできます。
root@controller:~/$ kubectl get nodes -o wide --show-labels | grep “os=windows”
akswin000000 Ready agent 4d17h v1.26.6 agentpool=win,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=windows…
akswin000001 Ready agent 4d17h v1.26.6 agentpool=win,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=windows…
root@controller:~/$
図 7:クラスター内の Windows ノードを表示するコマンド
これで、脆弱かどうかを簡単に判断できます。なお、「os=windows」の部分に注意してください。Windows ノードがない場合、コマンドを実行しても何も出力されません。つまり、脆弱性はないということです。
緩和
利用できる唯一の緩和策は、Kubernetes を 1.28.3 よりも新しいバージョンにして、パッチを適用することです。
とはいえ、組織やネットワークによっては、パッチを即座に適用できないこともあります。パッチを適用していない場合のリスクに対応するために、このようなふるまいを検知しブロックするための OPA ルールを用意しました。
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "PersistentVolume"
path := input.request.object.spec.local.path
contains(path, "&")
msg := sprintf("malicious path: %v was found", [path])
}
オープン・ソース・エージェント(OPA) は、ユーザーがノードを出入りするトラフィックに関するデータを受信し、受信したデータに対してポリシーベースのアクションを実行できるようにします。
この脆弱性は Windows ノードにのみ影響することに留意してください。Kubernetes クラスターに Windows ノードがなければ、この脆弱性を解消するために慌ててパッチを適用する必要はありません。しかし、時間があるときに、パッチを適用することが重要です。
この問題はソースコード内に潜んでいるため、この脅威に曝される危険性は依然としてあり、悪用される可能性が高まりそうです。したがって、Windows ノードがまったくないとしても、クラスターにパッチを適用することを強くお勧めします。
結論
この脆弱性は、セキュリティにおいて責任共有モデルが重要であることを示すよい例です。Kubernetes のソースコードに入力のサニタイズがないことを認識することで、深刻なセキュリティ上の影響を回避するための外部的な予防策を講じることができます。
2023 年だけで 7 つの異なるコマンドインジェクションの脆弱性が発見され、コードの他の場所にはさらに多くの脆弱性が潜んでいる可能性があります。ブルーチームとその組織は、この増加傾向に強い警戒感を持ち、YAML ファイルの内容を調べ、その中に隠れた脅威が潜んでいるかどうか監視するようにすべきでしょう。そのような取り組みを行う際に、この投稿で紹介している OPA ルールをご活用ください。
Akamai Security Intelligence Group は、この脅威やこれに類似するその他の脅威を継続的に監視し、調査結果を公開します。このような脆弱性に関する最新情報やその他のセキュリティに関する調査結果を常に把握するためには、 X(旧 Twitter)で Akamai をフォロー してください。
Kubernetes チームの迅速な対応とスムーズなコミュニケーションに感謝します。
開示のタイムライン
2023/11/01 — Kubernetes チームに脆弱性を開示
2023/11/11 — Kubernetes チームが CVE を割り当て
2023/11/14 — Kubernetes が CVE 修正プログラムをリリース
2024/03/13 — 本ブログ記事公開