はじめに — 座学では届かない領域
こんにちは、中山( @k1nakayama ) です。
皆さんの組織では、セキュリティインシデントへの対応訓練をどのように行っていますか?ドキュメントベースのレビューや座学研修で済ませていないでしょうか。
AWS Well-Architected Frameworkのオペレーショナルエクセレンスの柱では、OPS 08(How do you manage workload and operations events?)において、GameDayの定期的な実施を通じたイベント対応手順とチーム対応力の検証を推奨しています。またセキュリティの柱でも、SEC 10(How do you anticipate, respond to, and recover from incidents?)でインシデントレスポンスの実践的な訓練の必要性が明記されています。
当社のクラウドパートナーグループでは毎月「Tech Challenge Day(TCD)」というスキルアップイベントを実施しており、これまではAWS Skill Builderを使ったJam形式で個別チャレンジに取り組んできました。しかしJamは「問題を解く」形式であるのに対し、実際の現場では「動いているサービスを守りながら、突然降りかかるインシデントに対処する」ことが求められます。
re:Invent等で開催されているGameDayはまさにその実践の場ですが、メンバー全員がそこに参加できるわけではありません。そこで今回のTCDでは初の試みとして、自社でGameDayプラットフォームを構築し、実際の脅威インテリジェンスに基づいた攻撃シナリオを再現するGameDayを実施しました。
本記事では、プラットフォームの設計・実装から実施結果、そこから得られたセキュリティ上の知見までを共有します。
攻撃シナリオの設計 — Sysdigの脅威レポートからの着想
GameDayのシナリオを「それっぽい架空の障害」ではなく「実際に観測された攻撃キルチェーン」にすることで、参加者が得る学びの質は大きく変わります。今回シナリオの基盤としたのは、2026年2月にSysdig Threat Research Team(TRT)が報告した「AI-assisted cloud intrusion achieves admin access in 8 minutes」というレポートです。
参考: https://www.sysdig.com/blog/ai-assisted-cloud-intrusion-achieves-admin-access-in-8-minutes
このレポートで報告されたキルチェーンは以下の通りです。
- 公開されたS3バケットから認証情報を窃取(初期アクセス)
- 窃取した認証情報でLambda、IAM、Secrets Manager等のAWSサービスを列挙(偵察)
- Lambda関数へのコード注入により管理者ユーザーに権限昇格(わずか8分で達成)
- 19の異なるAWSプリンシパルを侵害(横展開)
- Secrets Manager、SSMパラメータ、CloudTrailログの抽出(データ収集)
- Bedrock LLMモデルの無許可呼び出し — いわゆるLLMjacking(リソース悪用)
注目すべきは、攻撃者がAIを活用していた痕跡が確認されている点です。セルビア語コメント付きのLLM生成コード、実在しないGitHubリポジトリへの参照(ハルシネーション)など、AIアシストによる攻撃の自動化が進んでいることを裏付けるものでした。
GameDayシナリオへの落とし込み
このキルチェーンをGameDayで再現可能な5段階の攻撃ステップに再構成しました。

| Step | 攻撃名 | 内容 | 防御手段 |
|---|---|---|---|
| 1 | S3偵察 | 公開バケットの探索、認証情報の発見 | IaC Scanning → BlockPublicAccess有効化 |
| 2 | 認証情報の悪用 | 窃取キーでListBuckets, ListRoles等のAPI呼び出し | Secret Detection → キーローテーション |
| 3 | Lambda バックドア(権限昇格) | Lambda関数にバックドアコード注入 → Admin IAMユーザー自動作成 | IAM最小権限化 |
| 4 | セキュリティグループ改変 | バックドアキーでALBインバウンド削除 + 0.0.0.0/0開放 | バックドアキー無効化 |
| 5 | ECSサービス停止 | desired_count=0 + 60秒間隔の持続的妨害 | バックドアキー無効化 |
設計上のポイントは、Step 3で作成されたバックドアユーザーのアクセスキーがStep 4/5の攻撃に使われる「連鎖構造」にしたことです。参加チームがStep 3の段階でバックドアユーザーのアクセスキーを無効化すれば、以降の攻撃チェーン全体を断ち切ることができます。この構造により、「根本原因の特定と対処」がいかに重要かを体感できるシナリオになっています。
GameDayプラットフォームのアーキテクチャ
全体構成
「Operation CloudPlate」と名付けた今回のGameDayは、架空のスタートアップ「CloudPlate Inc.」が運営するオンラインフードデリバリーサービスを舞台としています。参加チームはこのサービスの運用チームとして、サービスの復旧・運用・インシデント対応に取り組みます。
プラットフォームはAWS Organizationsによるマルチアカウント構成で、以下の3要素から成り立っています。

| 構成要素 | 役割 | デプロイ先 |
|---|---|---|
| GameBoard | スコアリング、リーダーボード、イベントログ管理(FastAPI + React + DynamoDB) | 管理アカウント(ECS Fargate + ALB) |
| GMツール群 | 攻撃シミュレーション実行、防御状況チェック(Python/boto3) | GMのローカル環境 |
| ワークロード基盤 | チームが運用するCloudPlateサービス | 各チームのAWSアカウント |
管理アカウントからチームアカウントへはOrganizationAccountAccessRoleを用いたAssumeRoleでクロスアカウントアクセスを行い、GMツール(攻撃スクリプト)がチーム環境に対して攻撃シミュレーションを実行します。チーム毎に独立したAWSアカウントとGitLabリポジトリを提供し、互いの環境が干渉しない設計としました。
ワークロード基盤(チーム環境)
各チームのAWSアカウントにAWS CDK(Python)でデプロイするCloudPlateサービスは、3層のサブネット構成を取ります。

| レイヤー | サブネット | コンポーネント |
|---|---|---|
| フロントエンド | Public Subnet | ALB |
| アプリケーション | Private Subnet | ECS Fargate(CloudPlate API)、Lambda(order-processor)、VPC Endpoints |
| データベース | Isolated Subnet | Aurora PostgreSQL Serverless v2 |
このワークロードには、CDKの cdk.context に設定した inject_failures フラグにより、Phase 1用のインフラ障害とPhase 2以降の攻撃の伏線となるセキュリティ脆弱性を注入しています。
CDKによる障害注入(Failure Injection)の実装
同一のCDKテンプレートから「正常版」と「障害版」をデプロイできるよう、CloudFormation Conditionsを活用した障害注入メカニズムを実装しました。
# 例: Private SubnetのNAT Gatewayルートを障害注入時に無効化
if inject_failures:
nat_route = private_subnet.node.find_child("DefaultRoute")
cfn_route = nat_route.node.default_child
cfn_route.add_override("Condition", "NeverDeploy")
# CfnCondition("NeverDeploy", expression=Fn.condition_equals("never", "deploy"))注入する障害は以下の通りです。
Phase 1で参加チームが対処するインフラ障害:
| レイヤー | 障害内容 | 学習目標 |
|---|---|---|
| VPC | Private SubnetのNAT Gatewayルート欠落 | ルーティングテーブルの理解 |
| VPC | ECS SG → VPC Endpointへの443アウトバウンド未許可 | セキュリティグループとVPC Endpointの関係 |
| ECS | ALBヘルスチェックパスが /healthz(正しくは /health) | ALB + ターゲットグループの設定 |
| IAM | ECSタスクロールにSecrets Manager読み取り権限なし | IAMロールとタスク定義の関係 |
| RDS | SGがECSタスクからの5432インバウンドを未許可 | データベース接続のSG設計 |
Phase 2以降の攻撃の伏線となるセキュリティ脆弱性:
| 問題 | 検出方法(GitLabスキャン) | 攻撃ステップとの関連 |
|---|---|---|
| S3 BlockPublicAccessが無効 | IaC Scanning(KICS) | Step 1: S3偵察 |
| ソースコードにハードコードされたアクセスキー | Secret Detection(Gitleaks) | Step 2/3: 認証情報悪用、Lambda改変 |
| 脆弱なベースイメージ(python:3.13.0) | Container Scanning | セキュリティ品質向上 |
| 過剰なIAM権限(FullAccessポリシー) | IaC Scanning(KICS) | Step 3: Lambda権限昇格 |
ここで意図したのは、Phase 2でGitLab CI/CDのセキュリティスキャンを有効化すれば発見・修正できる脆弱性を、Phase 3の攻撃の足がかりとしている点です。参加チームがスキャンを通じて事前に脆弱性を修正していれば攻撃をブロックでき、未修正であれば攻撃が成功するという「動的に分岐するシナリオ」を実現しています。
リアルタイムスコアリングシステム
継続スコアリング — サービスの稼働がそのままスコアになる
GameDayの本質は「サービスの継続稼働」です。Jamのように問題を解いて終わりではなく、サービスが動いている時間がスコアに直結します。
GameBoardバックエンド(FastAPI + asyncio)内の監視デーモンが30秒間隔で各チームのALBエンドポイントにHTTPリクエストを送信し、正常応答が返ればエンドポイントごとの重みに応じたポイントを加算します。
| エンドポイント | 正常応答の条件 | 30秒毎の加点 |
|---|---|---|
| GET /health | HTTP 200 OK | +1 |
| GET /api/menu | HTTP 200 + 有効なJSON | +2 |
| POST /api/orders | HTTP 201 Created | +3 |
| GET /api/orders/{id} | HTTP 200 + 有効なJSON | +2 |
全エンドポイント正常時は30秒あたり+8点、1時間あたり最大+960点です。早くサービスを復旧させたチームほど多くの継続ポイントを稼ぐことができ、Phase 3ではエンドポイント異常時に重み×4の減点(全停止時 -32点/30秒)が発生するため、攻撃によるサービス停止のインパクトが一気に大きくなります。
DynamoDB シングルテーブル設計
GameBoardのデータ管理には DynamoDB のシングルテーブル設計を採用しました。イベント、チーム、イベントログ、監視結果、攻撃ステップ状態、スコアスナップショットを1テーブルで管理しています。
PK SK 用途
────────────────────────────────────────────────────────────
EVENT#{id} #METADATA イベント情報
EVENT#{id} TEAM#{id} チーム情報 + スコア
EVENT#{id} ATTACK#{step} 攻撃ステップ状態
EVENT#{id}#TEAM#{id} LOG#{ts}#{ulid} イベントログ
EVENT#{id}#TEAM#{id} MONITOR#{ts} 監視結果
EVENT#{id}#TEAM#{id} SNAPSHOT#{ts} スコアスナップショットスコア推移グラフ用のSNAPSHOTはSort Keyにタイムスタンプを使っており、begins_with + ScanIndexForward=false で効率的に時系列データを取得できます。
WebSocketによるリアルタイム配信
スコア変動やイベントログはWebSocket(FastAPI WebSocket)を介してリーダーボード画面にリアルタイム配信しています。大画面モニターに常時投影することで、会場全体でスコアの動きを共有でき、GameDayの臨場感を高めています。
ヒントシステムは明示的に用意せず、イベントログ自体がヒントとして機能する設計としました。たとえば 「GET /api/orders が 503 Service Unavailable を返しました。注文サービスに問題があります」 といったログから、チームが自ら状況を読み解いて対応する形です。
攻撃シミュレーションの実装 — 防御チェックと動的分岐
防御チェック → 分岐の設計パターン
このGameDayで最もこだわった実装が、攻撃ステップ実行前に参加チームの防御状況をチェックし、結果に応じて攻撃の成否が動的に分岐するアーキテクチャです。
class AttackStep(ABC):
def execute(self, team_name: str) -> AttackResult:
# 1. 防御状況の事前チェック
pre_check = self.pre_check(team_name)
# 2. 防御済みなら攻撃失敗として演出
if pre_check.is_defended:
self.report_defense_success(team_name, pre_check)
return AttackResult.DEFENSE_SUCCESS
# 3. 未防御なら攻撃実行
snapshot = self.save_snapshot(team_name) # ロールバック用
result = self._execute_attack(team_name)
self.report_attack_result(team_name, result)
return result各攻撃ステップで実行される防御チェックの内容:
| チェック項目 | 確認内容 | 対応する攻撃ステップ |
|---|---|---|
| S3 BlockPublicAccess | 4項目がすべてtrueか | Step 1 |
| アクセスキーの有効性 | ハードコードキーでSTS GetCallerIdentityが成功するか | Step 2, 3 |
| IAM権限の状態 | CloudPlate関連ロールにFullAccess系ポリシーがあるか | Step 3 |
| バックドアキーの有効性 | Step 3で作成されたキーでAPI呼び出しが可能か | Step 4, 5 |
この設計により、全チームが同じ攻撃を受けるのではなく、各チームのセキュリティ対策の進捗に応じてシナリオが異なる展開を見せます。同じGameDayでありながら、チーム毎にストーリーが分岐する体験を実現できました。
スコアリングモデルの設計思想
Well-Architected セキュリティの柱の予防的統制(Preventive Controls)を重視する思想を反映し、事前防御が事後対応よりもやや有利になるスコアリングを設計しました。
| 項目 | 事前防御ルート | 事後対応ルート |
|---|---|---|
| セキュリティ事前防御 | 最大 +460点 | — |
| 防御成功ボーナス | 最大 +800点 | — |
| インシデント事後対応 | — | 最大 +800点 |
| 攻撃による直接減点 | ±0点 | -410点 |
| Phase 3 異常時減点 | ±0点 | -320〜-640点 |
| 実質スコアへの影響 | 最大 +1,260点 | -250〜+70点 |
全スコアの50%以上を継続ポイントが占めるため、「サービスの安定稼働を維持する」ことが最も重要なスコア源となっています。派手なボーナスを狙うよりも、地道にサービスを動かし続けたチームが勝つ設計です。
GitLab CI/CDセキュリティスキャンとの連携
チームに配布するGitLabリポジトリの .gitlab-ci.yml には、セキュリティスキャンジョブを初期状態ではコメントアウトした状態で含めています。また、stagesの定義に意図的なtypo(security-scan を security_scan と不一致にする)を仕込み、チーム自らがパイプラインを修復・拡張する過程で以下のスキャンを有効化する構成です。
- Secret Detection(Gitleaks): ソースコード内のハードコードされたアクセスキーを検出
- SAST: 静的解析によるコード品質の向上
- Container Scanning: ベースイメージ(python:3.13.0)の脆弱性を検出
- IaC Scanning(KICS): CloudFormationテンプレート内のS3パブリックアクセス設定、過剰IAM権限を検出
- Dependency Scanning: 依存パッケージの脆弱性チェック
「スキャンで発見 → 修正 → 攻撃をブロック」という一連の流れを体験することで、CI/CDパイプラインにセキュリティスキャンを組み込むこと——いわゆるシフトレフトの実効性を、スコアという形で定量的に実感できる設計としました。
実施結果と振り返り
2チーム(計5名)で実施した結果を振り返ります。
タイムテーブル
| 時間 | Phase | 内容 |
|---|---|---|
| 10:40 – 11:00 | — | ルール説明・環境配布 |
| 11:00 – 12:00 | Phase 1 | サービス復旧(インフラ障害の修復) |
| 12:00 – 13:00 | — | ランチ休憩(中間スコア発表) |
| 13:00 – 14:30 | Phase 1延長 | サービス復旧の継続 |
| 14:30 – 15:00 | Phase 2 | CI/CD整備 + セキュリティスキャン有効化 |
| 15:00 – 16:30 | Phase 3 | 攻撃シミュレーション(Step 1〜5を段階的に実行) |
| 16:30 – 17:00 | — | スコア確定 |
| 17:00 – 18:00 | — | 振り返り・結果発表 |
全体の流れ
当初のタイムテーブルではPhase 1(サービス復旧)に1時間を想定していましたが、実際にはランチ休憩を挟んで14:30まで延長する形となりました。VPCのルーティングやセキュリティグループの設定など、今回重点的にカバーしたかったネットワークレイヤーの設定は、想定通り各チームにとって歯ごたえのあるチャレンジとなりました。
今回はAIコーディングアシスタントの使用を原則禁止としており、日常の開発で得られるAIサポートがない中での対応を求めました。この制約を設けた意図は後述しますが、普段とは異なるアプローチが求められる環境が、チームに多くの気づきをもたらすことになりました。
Phase 2〜3: CI/CD整備と攻撃シミュレーション
Phase 2以降では、チーム間でCI/CDの整備や攻撃への対応に差が出る結果となりました。一方のチームはPhase 3の攻撃に対し、終盤でバックドアユーザーのアクセスキーを無効化するところまで到達し、攻撃チェーンの断ち切りに成功しています。もう一方のチームはPhase 1の復旧に注力した結果、Phase 3の攻撃への事前防御を十分に整える時間を確保できませんでした。
この差が生まれた要因はさまざまですが、GameDayの設計意図としては「チーム間の優劣をつけること」ではなく「どの領域に対してチームとしての対応力を伸ばす余地があるかを可視化すること」にあります。今回のGameDayはまさにその目的を果たす結果となりました。
得られた知見
1. VPC Endpointによるアウトバウンド制限がもたらすセキュリティ効果
今回の攻撃シナリオでStep 3のLambda改変が成功した後、改ざんされたLambda関数はAdministratorAccessを持つIAMユーザーを作成するためにIAM APIへの通信を行います。
今回のワークロードでは、Private Subnet内のLambda関数はNAT Gatewayを経由してインターネットにアクセスできる構成でした。しかし仮に、NAT Gatewayへのルートを設けず、代わりにECRやSecrets ManagerへのVPC Endpointのみを配置していた場合どうなるか。
Lambda関数は改ざんされてもIAM APIへの通信経路が存在しないため、バックドアユーザーの作成自体が失敗します。つまり、攻撃のキルチェーン全体がStep 3の時点で断ち切られることになります。

上図はNAT Gateway経由のパターン(現構成)とVPC Endpointのみのパターン(推奨)を比較したものです。NAT Gateway経由ではLambdaからIAM APIへの通信が可能なため権限昇格が成立しますが、VPC Endpointのみの構成ではIAM APIへのルートが存在せず、攻撃が途中で頓挫します。
これは、ビジネスロジックに必要なAWSサービスへの通信のみをVPC Endpointで許可し、不要なインターネットへのアウトバウンドアクセスを遮断する「Secure Subnet」設計の有効性を端的に示しています。最小権限の原則はIAMだけでなく、ネットワーク経路にも適用すべきという気づきが得られました。
2. シフトレフトの定量的な効果
スコアリングモデルの設計意図通り、事前防御ルートと事後対応ルートのスコア差は歴然でした。CI/CDパイプラインにセキュリティスキャンを早期に組み込み、検出された脆弱性を修正しておくことで、Phase 3の攻撃を受けた際のスコアへの影響を大幅に軽減できることが、実際のスコアとして可視化されました。
「セキュリティスキャンを先にやっておけばよかった」——振り返りで参加者から出たこの言葉が、まさにこのGameDayで意図した学びです。
3. AI時代におけるクラウド基礎力の再確認
今回AIの使用を原則禁止としたのは、「AIを使えない状況を作ること」自体が目的ではなく、日常的にAIを活用している中では見えにくくなっている「自分たちがどの領域の知識を自前で持っているか」を棚卸しする機会としたかったためです。
ルーティングテーブルの仕組み、セキュリティグループのステートフル性、VPC Endpointの動作原理——こうした基礎を理解した上でAIを活用するのと、基礎を持たずにAIの出力をそのまま受け入れるのとでは、トラブルシューティングの質に差が出ます。AIツールが高度化するほど、それを使いこなす側の基礎力が問われるという当たり前の事実を、チーム全体で改めて共有できた点は大きな収穫でした。
今後の展望
AIを活用したGameDayへの発展
今回はAI禁止の制約を設けましたが、次回はAIの使用を許可した上で実施する方向を考えています。AIを活用しつつも、その提案を適切に評価・判断できるか、つまり「AIと協働する力」を測るGameDayです。
AIが提示するトラブルシューティング手順を正しく評価する力、出力を検証する姿勢は、AI時代のエンジニアに求められるスキルセットです。今回の「AI禁止」で得た気づきを踏まえて、次は「AI活用」の中でどのような学びが生まれるかを検証するのは、自然な発展形だと考えています。
プラットフォームの再利用と拡張
CDKの inject_failures コンテキストフラグと攻撃ステップの分岐設計により、シナリオの追加・変更は容易です。ワークロードの種類(コンテナ以外にもサーバーレスやEKSなど)や攻撃パターンを変えることで、異なるスキル領域にフォーカスしたGameDayを構築できます。
四半期ごとにシナリオを変えて定期実施し、チームの対応力の推移を追跡していくことで、Well-Architected Reviewにおける「運用イベントの実施実績」としても活用できる基盤になると考えています。
まとめ
自社でGameDayプラットフォームを構築・実施した結果、以下の点を改めて確認できました。
座学やJam形式では得られない「動いているサービスを守る」体験は、チームのインシデント対応力を底上げする上で大きな効果があること。実際の脅威インテリジェンスに基づくシナリオは、参加者の危機意識を高め、セキュリティ対策の優先度判断に直接つながること。そして、CI/CDパイプラインへのセキュリティスキャン統合(シフトレフト)が「スコア」として目に見える形で効果を発揮することで、その必要性を体感的に理解できること。
VPC Endpointを活用したSecure Subnet設計によるアウトバウンド制限の有効性は、今回のGameDayを通じて得られた具体的かつ実践的な知見です。最小権限の原則はIAMポリシーだけでなく、ネットワーク経路設計にも適用すべきものであり、この気づきは今後のアーキテクチャ設計に直接反映できるものです。
GameDayは準備に相応の工数がかかりますが、CDKによるIaC化とスクリプトによる自動化により、再現可能なプラットフォームとして構築すれば、継続的な実施と改善のサイクルを回すことができます。
自社のチームが直面しうる脅威に即したGameDayの実施を検討されている方の参考になれば幸いです。