kubeletのCVE-2021-25741を緩和する方法: Symlink exchangeでホストファイルシステムへのアクセスが許可される

By 清水 孝郎 - SEPTEMBER 27, 2021

SHARE:

本文の内容は、2021年9月24日にAlberto Pellitteriが投稿したブログ(https://sysdig.com/blog/cve-2021-25741-kubelet-falco)を元に日本語に翻訳・再構成した内容となっております。

CVE-2021-25741は、Kubernetesにおいて新たに発見された脆弱性で、subPathボリュームマウントを持つコンテナを作成すると、ホストファイルシステムを含むボリューム外のファイルとディレクトリへのアクセスが可能となります。

2021年9月に公開されたもので、Kubernetesの各ノードで動作するノードエージェントであるkubeletに影響します。特にCVE-2021-25741は、以下のKubernetesバージョンのkubeletに影響します。

  • v1.22.0 – v1.22.1
  • v1.21.0 – v1.21.4
  • v1.20.0 – v1.20.10
  • <= v1.19.14
この問題は、Kubernetesのリポジトリに公開された公式の問題で報告されているように、攻撃の複雑さが低いことと、潜在的な影響が大きいことから、深刻度が高い(CVSS 8.8)とされています。この問題を利用してホストのファイルシステムにアクセスすることができたユーザーは、Kubernetesノードだけでなく、実行中の他のすべてのコンテナ(もしあれば)も危険にさらすことができます。つまり、ホストの構成が変更され、そのデータが流出、変更、削除される可能性があるということです。つまり、あらゆるデータの機密性、完全性、可用性(CIA)が危険にさらされる可能性があります。

この脆弱性の実際の影響をよりよく理解するために、CVE-2021-25741をどのように軽減し、検出するかを検討します。

予備知識:Kubernetesにおけるボリューム

Kubernetesのボリュームは、コンテナストレージの刹那的な性質に関連するさまざまな問題を解決します。

コンテナを実行し、そのファイルシステム内に新しいファイルを直接保存すると、コンテナがクラッシュした場合、そのファイルを失うことになります。しかし、ボリュームを使用すれば、コンテナやポッドの寿命を超えてデータを保存することができます。

また、同じポッド内の複数のコンテナからファイルにアクセスできるようにしたい場合もあります。ボリュームを使用すると、一貫性のある方法でこれを実現できます。

つまり、ボリュームはポッドのコンテナからアクセス可能なディレクトリです。これらのボリュームにはさまざまなタイプがあり、起動時にポッドの仕様で直接指定します。これらのボリュームタイプの1つがsubPathです。これは通常、1つのポッド内の異なるコンテナ間で1つのボリュームを複数の目的で共有したい場合に使用されます。これは、CVE-2021-25741の影響を受けるシナリオであり、この記事で取り上げるシナリオでもあります。

subPathマウントはどのように機能しますか?

コンテナが起動すると、kubeletボリュームマネージャーは、ホストシステム上の専用ディレクトリの下に、ポッドで指定された、必要なすべてのボリュームをローカルにマウントします。ボリュームのマウントに関する情報は、その後、必要に応じてコンテナに戻されます:
  • コンテナ内のボリュームのパス
  • ホスト上のボリュームのパス
このようにして、コンテナは起動時にコンテナルートファイルシステムにパスを作成し、提供されたホストパスにマウントするようにバインドすることができます。これにより、コンテナのルートファイルシステムがホストのルートファイルシステムに複製され、変更があればすぐに反映されることになります。

CVE-2021-25741 の問題点

この脆弱性によって生じる問題は、utils-linux のマウントにおけるシンボリックリンクの解決に関連しています。実は、K8sではマウントのsyscallを直接使用せず、シンボリックリンクを解決するutils-linuxのsyscallを使用しています。

そのため、Volume Subpathを使う場合、Kubernetesはボリュームをマウントした後、Subpathのバインドマウントを行い、ランタイムに渡します。ちなみに、マウント操作は作成されたコンテナによって制御されるため、悪意のあるユーザーが行うこともあるので、シンボリックリンクの交換は防ぐべきです。

ここでは、この脆弱性の背後にある真の問題とその解決策について報告しています:

Screenshot of the kubelet code showing how MountSensitiveWithoutSystemd was being used instead of MountSensitiveWithoutSystemdWithMountFlagsMountSensitiveWithoutSystemdWithMountFlags の代わりに MountSensitiveWithoutSystemd が使用されていることを示す kubelet コードのスクリーンショット

古い関数「MountSensitiveWithoutSystemd」(赤)が「MountSensitiveWithoutSystemdWithMountFlags」(緑)に置き換えられ、新たに「–no-canonicalize」という文字列のパラメータが追加されています。これは、ドキュメントでも報告されているように、utils-linuxのマウントは、デフォルトですべてのパスを正規化するからです。

-c, --no-canonicalize
Don't canonicalize paths. The mount command canonicalizes 
	all paths (from the command line or fstab) by default...

悪意のあるユーザーがこの脆弱性を悪用して得られるものをよりよく理解するために、ここではこの脆弱性の潜在的な影響を探っていきます。

CVE-2021-25741の影響

CVSSシステムによると、これは深刻度の高いセキュリティ問題で、スコアは8.8です。

この脆弱性に対する公開されたエクスプロイトはまだリリースされていませんが、この高いスコアリングは、攻撃の複雑さが低いことと厳密に関係しています。

さらに重要なことは、CVE-2021-25741の影響が非常に深刻であるということです。ホストのファイルシステムにアクセスできるということは、ホスト自体の制御権を得ることであり、他のコンテナに関連するデータが含まれている可能性のあるホストデータにもアクセスできるということです。これにより、悪意のあるユーザーがホスト環境を操作し、そのファイルシステム内のデータにアクセス、改ざん、削除することができる可能性があります。

ちなみに、この脆弱性は、コンテナがrootで実行されている場合にのみ悪用されるようです。つまり、幸いなことに、コンテナを起動しようとするユーザーには何らかの権限が必要となります。

最も深刻な影響を受ける可能性があるのは、hostPathマウントを作成する機能が制限されている環境です。
これは、CVE の悪用によってその制限がバイパスされ、hostPath 機能を使用せずに hostPath のようなアクセスが可能になるためです。


CVE-2021-25741 の修復

CVE-2021-25741 は以下の Kubernetes のバージョンで修正されています:
  • v1.22.2
  • v1.21.5
  • v1.20.11
  • v1.19.15

したがって、あなたの環境がこの脆弱性の影響を受けている場合は、できるだけ早くKubernetesのバージョンを更新する必要があります。

直ちに修復することができない場合は、他の戦略を採用することでリスクを軽減することができます。ここでは、この脆弱性の影響を軽減し、悪用を検知する方法をご紹介します。

CVE-2021-25741を軽減する方法

公式のKubernetesリポジトリで提案されているように、この脆弱性を緩和するためには、kubeletとkube-apiserverでボリュームサブパス機能を無効にし、既に使用しているポッドを削除することができます。

しかし、別の解決策として、OPAをアドミッションコントローラーとして導入することもできます。

アドミッションコントローラーレベルでの軽減

Kubernetesのアドミッションコントローラは、K8s APIへのリクエストをインターセプトして処理し、セマンティックオブジェクトの検証を実施して、作成したいオブジェクトの永続化の前に、リクエストがオーソライズされて認証された後に、リクエストを処理します。そのため、例えば、クラスターのリソースが不足していたり、イメージが安全でなかったり、ポッドやデプロイメントに付与された権限が尊重されていない場合、ACはポッドの実行をブロックすることができます。

Kubernetesのアドミッションコントローラーについては、以下の記事で詳しく紹介しています。
5分でわかるKubernetesのアドミッションコントローラー →
アドミッションコントローラでイメージスキャンを利用し、Kubernetesランタイムを保護→

Architecture of OPA Gatekeeper and how it communicates with the Kubernetes apiserverOPA GatekeeperのアーキテクチャとKubernetes apiserverとの通信方法

OPA Gatekeeperをアドミッションポリシーの仕組みとして採用することで、特定の制限をかけるポリシーを作成することができます。

CVE-2021-25741は、ユーザがroot権限でSubpathフィールドを持つコンテナをデプロイすると悪用される可能性があるため、以下のようなポリシーを実施するためにOPAを活用してみることができます:

OPA policy to block deploys of containers with root permissions and SubPath spec.
そこで、root権限と”SubPath”specのコンテナをデプロイすると、以下のように出力されます。

Error from server: error when creating "test-pod.yaml": admission webhook "validating-webhook.openpolicyagent.org" denied the request: Container nginx may exploit CVE-2021-25741.

CVE-2021-25741 の悪用前と悪用後の検知について

このCVEに対する公開されたエクスプロイトがリリースされていないとしても、脆弱なシステムに関連する不審な活動を監視することは重要です。ルートコンテナのデプロイメントや、ホストのファイルシステムへのアクセスがあった場合にアラートを出すことで、侵害の可能性を示すいくつかの行動を検出することができます。これらの不審なイベントを検出するために、Falcoを使用します。

FalcoはCNCFのオープンソースプロジェクトで、アプリケーションの予期せぬ動作を検出し、ランタイムでアラートを送信するために使用されます。これは、形式的でシンプルな言語に基づいており、事前に定義されたルールのセットを備えています。そして、これらのデフォルトのルールは、あなたの環境でターゲットを絞った検出を行うために、カスタマイズや拡張が可能です。

ここでは、いくつかの不審な動作を監視するためのデフォルトのFalcoルールを見てみましょう。

Falcoルール:ルートユーザーとしてコンテナを実行

前述したように、rootとしてコンテナを実行すると、悪意のあるユーザーがCVE-2021-25741を利用できる可能性があります。このようなコンテナの存在を検出するには、次のルールを使用できます。

- rule: Container Run as Root User
  desc: Detected container running as root user
  condition: spawned_process and container and proc.vpid=1 and user.uid=0 and not user_known_run_as_root_container
  enabled: false
  output: Container launched with root user privilege (uid=%user.uid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
  priority: INFO
  tags: [container, process]

Falcoルール:信頼されていない機密ファイルの読み取り

このルールは、ユーザー/パスワード情報などの機密ファイルの読み取りを検出します。

- rule: Read sensitive file untrusted
  desc: >
    an attempt to read any sensitive file (e.g. files containing user/password/authentication information). Exceptions are made for known trusted programs.
  condition: >
    sensitive_files and open_read
    and proc_name_exists
    and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries,
     cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries,
     vpn_binaries, mail_config_binaries, nomachine_binaries, sshkit_script_binaries,
     in.proftpd, mandb, salt-minion, postgres_mgmt_binaries,
     google_oslogin_
     )
    and not cmp_cp_by_passwd
    and not ansible_running_python
    and not run_by_qualys
    and not run_by_chef
    and not run_by_google_accounts_daemon
    and not user_read_sensitive_file_conditions
    and not mandb_postinst
    and not perl_running_plesk
    and not perl_running_updmap
    and not veritas_driver_script
    and not perl_running_centrifydc
    and not runuser_reading_pam
    and not linux_bench_reading_etc_shadow
    and not user_known_read_sensitive_files_activities
    and not user_read_sensitive_file_containers
  output: >
    Sensitive file opened for reading by non-trusted program (user=%user.name user_loginuid=%user.loginuid program=%proc.name
    command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
  priority: WARNING
  tags: [filesystem, mitre_credential_access, mitre_discovery]

Falcoルール:プライベートキーまたはパスワードの検索

攻撃者が機密データやファイルを見つけようとしているかどうかを検出できるようにします。

- rule: Search Private Keys or Passwords
  desc: >
    Detect grep private keys or passwords activity.
  condition: >
    (spawned_process and
     ((grep_commands and private_key_or_password) or
      (proc.name = "find" and (proc.args contains "id_rsa" or proc.args contains "id_dsa")))
    )
  output: >
    Grep private keys or passwords activities found
    (user=%user.name command=%proc.cmdline container_id=%container.id container_name=%container.name
    image=%container.image.repository:%container.image.tag)
  priority:
    WARNING
  tags: [process, mitre_credential_access]

Falcoルール:ssh情報の読み取り

このルールは、sshディレクトリ以下の読み取りの試みを検出を試みます。

- rule: Read ssh information
  desc: Any attempt to read files below ssh directories by non-ssh programs
  condition: >
    (consider_ssh_reads and
     (open_read or open_directory) and
     (user_ssh_directory or fd.name startswith /root/.ssh) and
     (not proc.name in (ssh_binaries)))
  output: >
    ssh-related file/directory read by non-ssh program (user=%user.name
    command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline)
  priority: ERROR
  tags: [filesystem, mitre_discovery]
Add a new Falco rule

まとめ

CVE-2021-25741 により、ユーザが root としてコンテナを作成し、 subpathボリュームをマウントすることができれば、 いつでもホストファイルシステムにアクセスすることができます。これは、ノードの機密データの漏洩につながり、潜在的にはノード自体や他のコンテナの危険性にもつながります。

この脆弱性の影響を受けているKubernetesクラスターは、ためらわずに新しいバージョンにアップデートしてください。そうしないと、クラスターの整合性やデータが危険にさらされる可能性があります。

CVE-2021-25741の影響を軽減したい場合は、アドミッションコントローラーを使用することができます。そして、侵入後の不審な行動を検出するために、Falcoのデフォルトルールまたはカスタムルールを採用することもできます。



Falcoについてもっと知りたい方は、こちらをご覧ください:

Sysdig Secureでは、他のオープンソースプロジェクトとともに、アウトオブボックスのルールでFalcoを拡張し、Kubernetesのセキュリティとの連携や管理をさらに容易にしています。30日間の無料トライアルに登録して、ご自身の目で確かめてください!