Docker コンテナフォレンジック分析
使用するタイミング
- 侵害されたDockerコンテナまたはコンテナホストを調査する場合
- レジストリからプルされた悪意のあるDockerイメージを分析する場合
- コンテナ化されたアプリケーションの侵害を含むインシデント対応時
- コンテナエスケープの試行または権限昇格を調査する場合
- コンテナ設定の監査と誤構成の特定を行う場合
前提条件
- フォレンジックワークステーションでのDocker CLIアクセス
- Dockerホストファイルシステムへのアクセス(フォレンジックイメージまたはライブ)
- Dockerレイヤードファイルシステム(overlay2、aufs)の理解
- イメージ分析のためのdive、docker-explorer、container-diff
- Dockerデーモン設定とソケットセキュリティの知識
- コンテナイメージの脆弱性スキャンのためのTrivyまたはGrype
ワークフロー
ステップ 1: コンテナの状態と証拠を保全する
# すべてのコンテナを一覧表示(停止中を含む)
docker ps -a --no-trunc > /cases/case-2024-001/docker/container_list.txt
# 侵害されたコンテナを調査
CONTAINER_ID="abc123def456"
docker inspect $CONTAINER_ID > /cases/case-2024-001/docker/container_inspect.json
# コンテナファイルシステムをtarballとしてエクスポート(現在の状態を保存)
docker export $CONTAINER_ID > /cases/case-2024-001/docker/container_export.tar
# コンテナの現在の状態からイメージを作成
docker commit $CONTAINER_ID forensic-evidence:case-2024-001
docker save forensic-evidence:case-2024-001 > /cases/case-2024-001/docker/container_image.tar
# コンテナログを取得
docker logs $CONTAINER_ID --timestamps > /cases/case-2024-001/docker/container_logs.txt 2>&1
# 実行中のプロセスを取得(コンテナがまだ実行中の場合)
docker top $CONTAINER_ID > /cases/case-2024-001/docker/container_processes.txt
# ネットワーク接続を取得
docker exec $CONTAINER_ID netstat -tlnp 2>/dev/null > /cases/case-2024-001/docker/container_network.txt
# コンテナから特定のファイルをコピー
docker cp $CONTAINER_ID:/var/log/ /cases/case-2024-001/docker/container_var_log/
docker cp $CONTAINER_ID:/tmp/ /cases/case-2024-001/docker/container_tmp/
docker cp $CONTAINER_ID:/etc/passwd /cases/case-2024-001/docker/container_passwd
# すべてのエクスポートされた証拠をハッシュ化
sha256sum /cases/case-2024-001/docker/*.tar > /cases/case-2024-001/docker/evidence_hashes.txt
ステップ 2: コンテナイメージレイヤを分析する
# イメージレイヤ分析のためのdiveをインストール
wget https://github.com/wagoodman/dive/releases/latest/download/dive_linux_amd64.deb
sudo dpkg -i dive_linux_amd64.deb
# イメージレイヤを対話的に分析
dive forensic-evidence:case-2024-001
# 非対話的なレイヤ分析
dive forensic-evidence:case-2024-001 --ci --json /cases/case-2024-001/docker/dive_analysis.json
# 個々のレイヤを抽出して調査
mkdir -p /cases/case-2024-001/docker/layers/
tar -xf /cases/case-2024-001/docker/container_image.tar -C /cases/case-2024-001/docker/layers/
# イメージマニフェストとレイヤ順序を一覧表示
cat /cases/case-2024-001/docker/layers/manifest.json | python3 -m json.tool
# 各レイヤの変更を調査
for layer in /cases/case-2024-001/docker/layers/*/layer.tar; do
echo "=== レイヤ: $(dirname $layer | xargs basename) ==="
tar -tf "$layer" | head -20
echo "..."
done
# container-diffを使用して元のベースイメージと比較
# container-diffをインストール
curl -LO https://storage.googleapis.com/container-diff/latest/container-diff-linux-amd64
chmod +x container-diff-linux-amd64
# コミットされたイメージを元のイメージと比較
./container-diff-linux-amd64 diff daemon://nginx:latest daemon://forensic-evidence:case-2024-001 \
--type=file --type=apt --type=history --json \
> /cases/case-2024-001/docker/container_diff.json
ステップ 3: Dockerホストアーティファクトを調査する
# Dockerデータディレクトリ(デフォルト: /var/lib/docker/)
DOCKER_ROOT="/mnt/evidence/var/lib/docker"
# overlay2ファイルシステムレイヤを調査
ls -la $DOCKER_ROOT/overlay2/
# コンテナのマージ済みファイルシステムを見つける
CONTAINER_HASH=$(docker inspect $CONTAINER_ID --format '{{.GraphDriver.Data.MergedDir}}' 2>/dev/null)
# またはフォレンジックイメージから手動で:
# /var/lib/docker/containers/<container_id>/config.v2.json を確認
# コンテナ設定ファイルを分析
cat $DOCKER_ROOT/containers/$CONTAINER_ID/config.v2.json | python3 -m json.tool \
> /cases/case-2024-001/docker/container_config.json
# Dockerデーモン設定を確認
cat /mnt/evidence/etc/docker/daemon.json 2>/dev/null > /cases/case-2024-001/docker/daemon_config.json
# Dockerイベントログを調査
cat $DOCKER_ROOT/containers/$CONTAINER_ID/*.log > /cases/case-2024-001/docker/container_json_logs.txt
# ボリュームマウントを確認(潜在的なホストファイルシステムアクセス)
python3 << 'PYEOF'
import json
with open('/cases/case-2024-001/docker/container_inspect.json') as f:
data = json.load(f)
inspect = data[0] if isinstance(data, list) else data
print("=== コンテナセキュリティ分析 ===\n")
# マウントを確認
print("ボリュームマウント:")
for mount in inspect.get('Mounts', []):
rw = "読み取り/書き込み" if mount.get('RW') else "読み取り専用"
print(f" {mount.get('Source', 'N/A')} -> {mount.get('Destination', 'N/A')} ({rw})")
if mount.get('Source') in ('/', '/etc', '/var', '/root') and mount.get('RW'):
print(f" 警告: 機密性の高いホストパスが読み取り/書き込みでマウントされています!")
# 特権モードを確認
host_config = inspect.get('HostConfig', {})
if host_config.get('Privileged'):
print("\n警告: コンテナは特権モードで実行されていました!")
# ケイパビリティを確認
cap_add = host_config.get('CapAdd', [])
if cap_add:
print(f"\n追加されたケイパビリティ: {cap_add}")
dangerous_caps = ['SYS_ADMIN', 'SYS_PTRACE', 'NET_ADMIN', 'SYS_MODULE']
for cap in cap_add:
if cap in dangerous_caps:
print(f" 警告: 危険なケイパビリティ: {cap}")
# PID名前空間を確認
if host_config.get('PidMode') == 'host':
print("\n警告: コンテナはホストPID名前空間を共有しています!")
# ネットワークモードを確認
if host_config.get('NetworkMode') == 'host':
print("\n警告: コンテナはホストネットワーク名前空間を共有しています!")
# ユーザーを確認
user = inspect.get('Config', {}).get('User', 'root (デフォルト)')
print(f"\n実行ユーザー: {user}")
# 環境変数から機密情報を確認
env_vars = inspect.get('Config', {}).get('Env', [])
print(f"\n環境変数: {len(env_vars)}件")
for env in env_vars:
key = env.split('=')[0]
if any(s in key.upper() for s in ['PASSWORD', 'SECRET', 'KEY', 'TOKEN', 'CREDENTIAL']):
print(f" 機密: {key}=***伏せ字***")
PYEOF
ステップ 4: コンテナファイルシステムの変更を分析する
# コンテナファイルシステムを元のイメージと比較
docker diff $CONTAINER_ID > /cases/case-2024-001/docker/filesystem_changes.txt
# A = 追加、C = 変更、D = 削除
# 変更を分析
python3 << 'PYEOF'
added = []
changed = []
deleted = []
with open('/cases/case-2024-001/docker/filesystem_changes.txt') as f:
for line in f:
line = line.strip()
if line.startswith('A '):
added.append(line[2:])
elif line.startswith('C '):
changed.append(line[2:])
elif line.startswith('D '):
deleted.append(line[2:])
print(f"追加されたファイル: {len(added)}個")
print(f"変更されたファイル: {len(changed)}個")
print(f"削除されたファイル: {len(deleted)}個")
# 不審な追加をフラグ付け
suspicious = [f for f in added if any(s in f for s in
['/tmp/', '/dev/shm/', '/root/', '.sh', '.py', '.elf', 'reverse', 'shell', 'backdoor'])]
if suspicious:
print(f"\n不審な追加ファイル:")
for f in suspicious:
print(f" {f}")
# 不審な変更をフラグ付け
sus_changed = [f for f in changed if any(s in f for s in
['/etc/passwd', '/etc/shadow', '/etc/crontab', '/etc/ssh', '.bashrc'])]
if sus_changed:
print(f"\n不審な変更ファイル:")
for f in sus_changed:
print(f" {f}")
PYEOF
# コンテナエクスポートを抽出して調査
mkdir -p /cases/case-2024-001/docker/container_fs/
tar -xf /cases/case-2024-001/docker/container_export.tar -C /cases/case-2024-001/docker/container_fs/
# ウェブシェルと悪意のあるファイルをスキャン
find /cases/case-2024-001/docker/container_fs/tmp/ -type f -exec file {} \;
find /cases/case-2024-001/docker/container_fs/ -name "*.php" -newer /cases/case-2024-001/docker/container_fs/etc/hostname
ステップ 5: 脆弱性をスキャンしてレポートを生成する
# 既知の脆弱性についてイメージをスキャン
trivy image forensic-evidence:case-2024-001 \
--format json \
--output /cases/case-2024-001/docker/vulnerability_scan.json
# エクスポートされたファイルシステムをスキャン
trivy fs /cases/case-2024-001/docker/container_fs/ \
--format table \
--output /cases/case-2024-001/docker/fs_vulnerabilities.txt
# イメージ内の機密情報を確認
trivy image forensic-evidence:case-2024-001 \
--scanners secret \
--format json \
--output /cases/case-2024-001/docker/secrets_scan.json
主要な概念
| 概念 | 説明 |
|---|---|
| イメージレイヤ | コンテナイメージを構成する読み取り専用のファイルシステムレイヤ |
| overlay2 | ユニオンファイルシステムを使用するデフォルトのDockerストレージドライバ |
| コンテナdiff | 元のイメージに対するランタイムファイルシステムの変更の比較 |
| 特権モード | ホストの全機能を持つコンテナ(ほとんどの分離をバイパス) |
| Dockerソケット | Dockerデーモンを制御するUnixソケット(/var/run/docker.sock) |
| コンテナエスケープ | コンテナの分離を突破してホストにアクセスする手法 |
| ボリュームマウント | コンテナ内部からアクセス可能にされたホストファイルシステムのパス |
| イメージ履歴 | 各レイヤを構築するために使用されたDockerfile命令の記録 |
ツールとシステム
| ツール | 目的 |
|---|---|
| docker inspect | 詳細なコンテナ設定と状態情報 |
| docker diff | 実行中または停止中のコンテナで行われたファイルシステム変更を表示 |
| dive | 対話型Dockerイメージレイヤ分析ツール |
| container-diff | コンテナイメージ内容を比較するGoogleのツール |
| Trivy | コンテナイメージとファイルシステムの脆弱性スキャナ |
| docker-explorer | オフラインのDockerアーティファクト分析のためのフォレンジックツール |
| Sysdig | コンテナランタイムセキュリティ監視とフォレンジック |
| Falco | コンテナとKubernetes向けのランタイム脅威検出 |
一般的なシナリオ
シナリオ 1: Webアプリケーションコンテナ侵害 コンテナファイルシステムをエクスポートし、Webルート内のウェブシェルを特定し、アクセスログを分析して悪用試行を確認し、追加されたファイルと変更された設定をチェックし、C2通信のためのネットワーク接続を調査し、エスカレーションパスのためのコンテナケイパビリティをレビューします。
シナリオ 2: 悪意のあるイメージによるサプライチェーン攻撃 diveを使用してイメージレイヤを分析し、どのレイヤが悪意のあるコンテンツを追加したかを特定し、container-diffを使用して公式のベースイメージと比較し、不審なRUNコマンドがないかイメージ履歴を確認し、埋め込まれたバックドアや暗号通貨マイナーをスキャンし、レジストリログからイメージプルを追跡します。
シナリオ 3: コンテナエスケープ調査 コンテナが特権モードまたは危険なケイパビリティで実行されたかどうかを確認し、ホストファイルシステムのマウントポイントへの不正アクセスを調査し、Docker-in-Dockerの悪用を可能にするDockerソケットマウントをレビューし、コンテナエスケープの兆候がないかホストシステムログを分析し、カーネルエクスプロイトアーティファクトを確認します。
シナリオ 4: コンテナ環境での暗号通貨マイニング CPU使用率の高いコンテナを特定し、マイニングバイナリがないかコンテナイメージをエクスポートして分析し、レジストリ内の許可されていないイメージを確認し、不正なデプロイメントのためにコンテナ作成イベントをレビューし、マイニングプール通信のためのネットワーク接続を調査します。
出力形式
Docker コンテナフォレンジックサマリー:
コンテナ: abc123def456 (nginx-app)
イメージ: company/web-app:v2.1
ステータス: 実行中 (開始 2024-01-10 09:00 UTC)
ホスト: docker-host-01.corp.local
セキュリティ設定:
特権モード: いいえ
追加されたケイパビリティ: NET_ADMIN (警告)
ボリュームマウント: /var/log -> /host-logs (読み取り/書き込み)
ネットワークモード: bridge
ユーザー: root (警告)
ファイルシステム変更:
追加: 23ファイル (5件不審)
変更: 12ファイル (2件不審)
削除: 0ファイル
不審な発見:
/tmp/reverse.sh - リバースシェルスクリプト (追加)
/var/www/html/.hidden/shell.php - PHPウェブシェル (追加)
/etc/crontab - 変更 (永続化cronエントリ追加)
/root/.ssh/authorized_keys - 変更 (不正なキー追加)
脆弱性スキャン:
クリティカル: 3 (ベースイメージのCVE-2024-xxxx)
高: 12
中: 34
証拠: /cases/case-2024-001/docker/