封じ込めは不可能:Kubernetes にコマンドインジェクションの脆弱性を発見
編集・協力:Tricia Howard
エグゼクティブサマリー
先日、Akamai のセキュリティ研究者である Tomer Peled が 重大度の高い脆弱性 を Kubernetes に発見し、この脆弱性(CVE-2023-3676)に 8.8 の CVSS スコアが付けられました。
この発見により、同じ根本原因を共有している脆弱性がさらに 2 つ特定されました。それは、安全でない関数呼び出しとユーザー入力のサニタイズの欠如です。
はじめに
YAML(YAML Ain't Markup Language を指す)は、主に設定ファイルで使用される JSON と同様のデータシリアライズ言語です。そのため、Kubernetes で大きな役割を果たします。Kubernetes は最近普及が進んでいるコンテナ・オーケストレーション・システムであり、コンテナ化されたアプリケーションの展開、拡張、管理を自動化するために役立ちます。Kubernetes フレームワークでは、Container Network Interface の構成からポッド管理、さらにはシークレット処理まで、基本的にすべてに YAML ファイルが使用されます。
Kubernetes の YAML ファイルは近年、研究のテーマとされてきました。Kubernetes がクラスター構成に関して YAML に依存していることを考慮すると、この研究テーマは深掘りすべき極めて興味深いものです。
YAML と Kubernetes はこれまでにどのように悪用されてきたのか
2022 年には CVE-2022-1471 が YAML ファイルの既知のパーサーである SnakeYAML のコンストラクター内で見つかりました。この脆弱性により、安全でないオブジェクトが作成され、そのオブジェクトを使用する脆弱なアプリケーションでコードが実行される可能性がありました。Kubernetes セキュリティチームが作成したこの CVE-2012-25749 に関する CVE アドバイザリー では、YAML ファイルが与え得る影響を示すもう 1 つの例を見ることができます。この脆弱性により、攻撃者は YAML ファイル内の名前をスペルミスして、ルートとして実行できるユーザーの検証を回避できます。
Kubernetes に関する調査の中で、私たちは CVE-2017-1002101 と CVE-2021-25741に遭遇しました。これらの脆弱性は、攻撃者が競合状態とシンボリックリンクを YAML ファイル内の subPath サブプロパティと組み合わせてコンテナ外の機密データにアクセスする方法を示しています。
このような過去の脆弱性が、私たちが Kubernetes コードベースでのパス処理の方向性を追求するきっかけとなり、最終的には今日話し合っている脆弱性の発見につながりました。
このブログ記事では、「適用」権限(Kubernetes API とのインタラクションに必要な権限)を持つ攻撃者が、SYSTEM 権限を持つリモートの Windows マシンで実行されるコードをどのように挿入できるかを説明します。
脆弱性の詳細
ポッドが作成される際、ユーザーにはポッドとホストの間に共有ディレクトリを作成するオプションがあります。この機能は ボリューム と呼ばれ、たとえばポッド内のコンテナを介して Web サイトを提供する場合などに便利です。ホストと共有されているディレクトリからサイトファイルを共有したい場合、このようにして望みどおりにサイトレイアウトを変更できます。サイトを含むイメージを毎回再コンパイルする必要はありません。
ボリュームの有効化は、ポッドの YAML ファイルに volume パラメーター を含めることによって行われます。ボリュームをマウントする場所は、 mountPath (コンテナ内の場所)および hostPath (ホスト上の場所)にリストされます。
最も重要なのは、subPath サブプロパティを使用して選択した場所にある共有ディレクトリまたはファイルをマウントできることです。図 1 にボリュームに関連するすべてのプロパティを示します。
YAML ファイルの解析は、 kubelet によって実行されます。kubelet とは、コンテナ化されたアプリケーションをノード上で実行する Kubernetes の主要サービスです。CVE-2021-25741 のパッチの一部として、kubelet は YAML ファイルのすべてのパラメーターを検証します。また、内部関数「isLinkPath」を呼び出して subPath パラメーターを使用した結果としてシンボリックリンクが作成されないようにします。図 2 はこの関数を表しています。
この関数は、ユーザーが YAML ファイルに入力した subPath をパラメーターとして受け取ります。次に、このパスを使用して、パスタイプ(シンボリックリンクかどうか)を判断するための PowerShell コマンドを作成します。その後、フォーマットされた PowerShell コマンドはすぐに「exec.Command」関数呼び出しによって呼び出されます。
「exec.Command」の存在とサニタイズされていないユーザー入力が組み合わさっていることは、コマンドインジェクションの機会があることを強く示唆します。
PowerShell を使用すると、ユーザーは文字列内の値を使用前に評価できます。これはたとえば、$(<experssion_to_be_evaluated>) を文字列に追加することによって行われます(図 3)。
PS> echo “the value of 1+1 is $(1+1)
Output:
the value of 1+1 is 2
図 3:文字列内の式を評価する PowerShell の例
この例は非常にシンプルですが、実際には 任意の PowerShell を括弧内に挿入し、評価することができます。たとえば、$(Start-Process cmd)、$(Invoke-Expression exp) などの PowerShell です。
攻撃者は、この subPath 評価を悪用して脆弱なコードに到達し、任意のコマンドを SYSTEM 権限 (kubelet 独自のコンテキスト)で リモートノードから実行することができ、クラスター内のすべての Windows ノードを掌握します。図 4 は悪性の subPath 値を示しています。
パッチ分析
Kubernetes チームは、ユーザー入力ではなく環境変数からパラメーターを渡すことで、このクラスの脆弱性にパッチを適用することを選択しました(図 5)。この方法で値を渡すことにより、パラメーターは文字列として扱われます。したがって、PowerShell によって式として評価されることはありません。
自分は脆弱か?
前述したように、1.28 以前のすべての Kubernetes バージョンはこの CVE に対して脆弱です。クラスター内のノードの 1 つが Windows ノードかどうかを確認するためには、いくつかの方法があります。1 つの方法は、図 6 のコマンドを実行することです。
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… |
図 6:クラスター内のノードの 1 つが Windows ノードかどうかを確認するための kubectl コマンド
緩和
パッチ適用は、この脆弱性から身を守るための最も信頼できる方法です。Akamai は、パッチを適用できない場合にこの脆弱性を阻止できる可能性のある方法をいくつか紹介しており、各自の環境に最適な方法を選択できます。
以前のソリューション
CVE-2023-3676 に関しては、Kubernetes 管理者が Volume.Subpathの使用を無効にできます。これにより、この脆弱性に対してクラスターの安全が確保され、本番環境のクラスターにとって重要な場合があるメカニズムが無効になります。
OPA
Open Policy Agent (OPA)は、ユーザーがノードを出入りするトラフィックに関するデータを受信し、受信したデータに対してポリシーベースのアクションを実行できるようにするオープン・ソース・エージェントです。OPA は Rego をエンジンの言語として使用します。管理者はそれを使用して特定の YAML ファイルの実装をブロックするルールを作成できます。図 7 は、悪性の subPath を持つポッドの作成を拒否するルールを表しています。
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
path := input.request.object.spec.containers.volumeMounts.subPath
not startswith(path, "$(")
msg := sprintf("malicious path: %v was found", [path])
}
図 7:悪性の subPath を持つポッドの作成を拒否するルール
RBAC
ロールベース・アクセス・コントロール(RBAC)は、ユーザーの操作をユーザーが誰であるかに応じてセグメント化する方法です。たとえば、各ユーザーは独自の名前空間にポッドを作成することだけ、または許可された名前空間の情報を閲覧することだけができます。これにより、クラスターでアクションを実行できるユーザーの数が制限され、悪用に関連する権限を持つユーザーに注意を向けることができます。
結論
CVE-2023-3676 に必要な権限は低いので、攻撃者にとってハードルが低くなります。必要なのは、ノードにアクセスして権限を 適用 することだけです。このブログ記事で説明したとおり、この脆弱性の悪用に成功すると、SYSTEM 権限を持つマシン上の任意の Windows ノードでリモートからコードが実行される可能性があります。
攻撃の容易さと強い影響力が組み合わさっていることは、通常、組織に対してこの攻撃(および同様の攻撃)が行われる可能性が高いことを意味します。実際、この脆弱性の制限要因は、現在はあまり一般的ではない Windows ノードに範囲が制限されていることだけです。
これらの脆弱性の悪用を防ぐためには、管理者が Kubernetes クラスターにパッチを適用し、最新バージョンにすることが重要です。パッチがすぐに利用できない場合は、上記のいずれかの方法を使用して、この脆弱性を防ぐことをお勧めします。
Kubernetes チームの迅速な対応とスムーズなコミュニケーションに感謝します。
開示のタイムライン
2023/07/13 — Kubernetes チームに脆弱性を開示
2023/07/19 — Kubernetes チームが CVE を割り当て
2023/08/23 — Kubernetes が CVE 修正プログラムをリリース
2023/09/13 — ブログ記事公開