Sysdigの脅威レポートを再現する自社GameDayを、AWS CDKとGitLabで構築・実施した話

AWS

はじめに — 座学では届かない領域

こんにちは、中山( @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

このレポートで報告されたキルチェーンは以下の通りです。

  1. 公開されたS3バケットから認証情報を窃取(初期アクセス)
  2. 窃取した認証情報でLambda、IAM、Secrets Manager等のAWSサービスを列挙(偵察)
  3. Lambda関数へのコード注入により管理者ユーザーに権限昇格(わずか8分で達成)
  4. 19の異なるAWSプリンシパルを侵害(横展開)
  5. Secrets Manager、SSMパラメータ、CloudTrailログの抽出(データ収集)
  6. Bedrock LLMモデルの無許可呼び出し — いわゆるLLMjacking(リソース悪用)

注目すべきは、攻撃者がAIを活用していた痕跡が確認されている点です。セルビア語コメント付きのLLM生成コード、実在しないGitHubリポジトリへの参照(ハルシネーション)など、AIアシストによる攻撃の自動化が進んでいることを裏付けるものでした。

GameDayシナリオへの落とし込み

このキルチェーンをGameDayで再現可能な5段階の攻撃ステップに再構成しました。

Step攻撃名内容防御手段
1S3偵察公開バケットの探索、認証情報の発見IaC Scanning → BlockPublicAccess有効化
2認証情報の悪用窃取キーでListBuckets, ListRoles等のAPI呼び出しSecret Detection → キーローテーション
3Lambda バックドア(権限昇格)Lambda関数にバックドアコード注入 → Admin IAMユーザー自動作成IAM最小権限化
4セキュリティグループ改変バックドアキーでALBインバウンド削除 + 0.0.0.0/0開放バックドアキー無効化
5ECSサービス停止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 SubnetALB
アプリケーションPrivate SubnetECS Fargate(CloudPlate API)、Lambda(order-processor)、VPC Endpoints
データベースIsolated SubnetAurora 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で参加チームが対処するインフラ障害:

レイヤー障害内容学習目標
VPCPrivate SubnetのNAT Gatewayルート欠落ルーティングテーブルの理解
VPCECS SG → VPC Endpointへの443アウトバウンド未許可セキュリティグループとVPC Endpointの関係
ECSALBヘルスチェックパスが /healthz(正しくは /healthALB + ターゲットグループの設定
IAMECSタスクロールにSecrets Manager読み取り権限なしIAMロールとタスク定義の関係
RDSSGが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 /healthHTTP 200 OK+1
GET /api/menuHTTP 200 + 有効なJSON+2
POST /api/ordersHTTP 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 BlockPublicAccess4項目がすべて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-scansecurity_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:00Phase 1サービス復旧(インフラ障害の修復)
12:00 – 13:00ランチ休憩(中間スコア発表)
13:00 – 14:30Phase 1延長サービス復旧の継続
14:30 – 15:00Phase 2CI/CD整備 + セキュリティスキャン有効化
15:00 – 16:30Phase 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の実施を検討されている方の参考になれば幸いです。

無料相談実施中
AWSを使用したサーバーレスアプリケーションの構築
サーバーレス開発内製化、AWSへの移行等
様々な課題について無料でご相談お受けいたします。
最新情報をチェックしよう!