[OSS] AWS CloudSaga を使って疑似セキュリティインシデントを起こしてみた

2022.11.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS 事業本部コンサルティング部のイシザワです。

今回は OSS の AWS CloudSaga を使って疑似インシデントを起こしてみようと思います。

(追記)アイキャッチを変更しました。

CloudSaga とは

CloudSaga とは AWS 環境で疑似的にセキュリティイベントを発生させるツールです。 ユースケースとして、自分の AWS 環境でセキュリティイベントを検知するための仕組みや調査するための仕組みがあるかをテストすることが挙げられます。

やってみた

まず、マネジメントコンソールから AWS CloudShell を起動します。

以下のコマンドで CloudSaga をインストールします。

$ pip3 install cloudsaga

インストールが完了したら、以下のコマンドで現在利用可能なセキュリティイベントのシナリオを一覧できます。

$ cloudsaga --chapters



     ___   ____    __    ____   _______.          
    /   \  \   \  /  \  /   /  /       |         
   /  ^  \  \   \/    \/   /  |   (----`       
  /  /_\  \  \            /    \   \         
 /  _____  \  \    /\    / .----)   |      
/__/     \__\  \__/  \__/  |_______/       

  ______  __        ______    __    __   _______       _______.     ___       _______      ___
 /      ||  |      /  __  \  |  |  |  | |       \     /       |    /   \     /  _____|    /   \ 
|  ,----'|  |     |  |  |  | |  |  |  | |  .--.  |   |   (----`   /  ^  \   |  |  __     /  ^  \ 
|  |     |  |     |  |  |  | |  |  |  | |  |  |  |    \   \      /  /_\  \  |  | |_ |   /  /_\  \ 
|  `----.|  `----.|  `--'  | |  `--'  | |  '--'  |.----)   |    /  _____  \ |  |__| |  /  _____  \  
 \______||_______| \______/   \______/  |_______/ |_______/    /__/     \__\ \______| /__/     \__\ 
                                                                                                                                               


            Joshua "DozerCat" McKiddy - Team DragonCat - AWS
            Type -h for help.
    

        Chapters:
        imds-reveal: IMDS Reveal discovers instances that using IMDS v1, which are vulnerable to the IMDSv1 attack vector.
        mining-bitcoin: Uses Amazon EC2 resources to simulate creation of Bitcoin mining.
        network-changes: Uses Amazon VPC resources to simulate network changes.
        iam-credentials: Attempts to grab the IAM credential report within the AWS account.
        public-resources: Checks Amazon RDS and Amazon S3 for resources that are public, as well as creates a public RDS instance.

基本的にコマンドを実行するたびに CloudSaga のテキストアートが表示されますが、次回以降は省略します。

以下のコマンドで選択したシナリオを実行します。今回は mining-bitcoin を実行してみます。

$ cloudsaga --scenario mining-bitcoin
    
2022-11-09 04:16:08,405 - INFO - Checking region ap-south-1 for existing EC2 Instances...
2022-11-09 04:16:08,405 - INFO - DescribeInstances API Call
2022-11-09 04:16:09,032 - INFO - Checking region ap-northeast-1 for existing EC2 Instances...
2022-11-09 04:16:09,032 - INFO - DescribeInstances API Call
2022-11-09 04:16:09,129 - INFO - Checking region ap-northeast-2 for existing EC2 Instances...
2022-11-09 04:16:09,129 - INFO - DescribeInstances API Call
2022-11-09 04:16:09,359 - INFO - Checking region ap-northeast-3 for existing EC2 Instances...
2022-11-09 04:16:09,359 - INFO - DescribeInstances API Call
2022-11-09 04:16:09,464 - INFO - Checking region ap-southeast-1 for existing EC2 Instances...
2022-11-09 04:16:09,464 - INFO - DescribeInstances API Call
2022-11-09 04:16:09,847 - INFO - Checking region ap-southeast-2 for existing EC2 Instances...
2022-11-09 04:16:09,848 - INFO - DescribeInstances API Call
2022-11-09 04:16:10,379 - INFO - Checking region ca-central-1 for existing EC2 Instances...
2022-11-09 04:16:10,380 - INFO - DescribeInstances API Call
2022-11-09 04:16:11,030 - INFO - Checking region eu-central-1 for existing EC2 Instances...
2022-11-09 04:16:11,030 - INFO - DescribeInstances API Call
2022-11-09 04:16:12,022 - INFO - Checking region eu-west-1 for existing EC2 Instances...
2022-11-09 04:16:12,022 - INFO - DescribeInstances API Call
2022-11-09 04:16:12,903 - INFO - Checking region eu-west-2 for existing EC2 Instances...
2022-11-09 04:16:12,904 - INFO - DescribeInstances API Call
2022-11-09 04:16:13,812 - INFO - Checking region eu-west-3 for existing EC2 Instances...
2022-11-09 04:16:13,812 - INFO - DescribeInstances API Call
2022-11-09 04:16:14,754 - INFO - Checking region eu-north-1 for existing EC2 Instances...
2022-11-09 04:16:14,755 - INFO - DescribeInstances API Call
2022-11-09 04:16:15,803 - INFO - Checking region sa-east-1 for existing EC2 Instances...
2022-11-09 04:16:15,804 - INFO - DescribeInstances API Call
2022-11-09 04:16:16,942 - INFO - Checking region us-east-1 for existing EC2 Instances...
2022-11-09 04:16:16,943 - INFO - DescribeInstances API Call
2022-11-09 04:16:17,631 - INFO - Checking region us-east-2 for existing EC2 Instances...
2022-11-09 04:16:17,632 - INFO - DescribeInstances API Call
2022-11-09 04:16:18,274 - INFO - Checking region us-west-1 for existing EC2 Instances...
2022-11-09 04:16:18,274 - INFO - DescribeInstances API Call
2022-11-09 04:16:18,780 - INFO - Checking region us-west-2 for existing EC2 Instances...
2022-11-09 04:16:18,780 - INFO - DescribeInstances API Call
2022-11-09 04:16:19,279 - INFO - Spinning up Bitcoin Miners (DryRun) in region ca-central-1...
2022-11-09 04:16:19,280 - INFO - RunInstances API Call
2022-11-09 04:16:20,035 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:20,046 - INFO - Spinning up Bitcoin Miners (DryRun) in region sa-east-1...
2022-11-09 04:16:20,047 - INFO - RunInstances API Call
2022-11-09 04:16:21,295 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:21,304 - INFO - Spinning up Bitcoin Miners (DryRun) in region eu-west-3...
2022-11-09 04:16:21,305 - INFO - RunInstances API Call
2022-11-09 04:16:22,397 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:22,404 - INFO - Spinning up Bitcoin Miners (DryRun) in region us-east-1...
2022-11-09 04:16:22,405 - INFO - RunInstances API Call
2022-11-09 04:16:23,776 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:23,784 - INFO - Spinning up Bitcoin Miners (DryRun) in region us-east-2...
2022-11-09 04:16:23,785 - INFO - RunInstances API Call
2022-11-09 04:16:24,856 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:24,865 - INFO - Spinning up Bitcoin Miners (DryRun) in region ap-northeast-1...
2022-11-09 04:16:24,865 - INFO - RunInstances API Call
2022-11-09 04:16:25,717 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:25,731 - INFO - Spinning up Bitcoin Miners (DryRun) in region us-west-1...
2022-11-09 04:16:25,732 - INFO - RunInstances API Call
2022-11-09 04:16:26,388 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:26,396 - INFO - Spinning up Bitcoin Miners (DryRun) in region eu-west-1...
2022-11-09 04:16:26,396 - INFO - RunInstances API Call
2022-11-09 04:16:27,742 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:27,751 - INFO - Spinning up Bitcoin Miners (DryRun) in region us-west-2...
2022-11-09 04:16:27,751 - INFO - RunInstances API Call
2022-11-09 04:16:28,783 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:28,793 - INFO - Spinning up Bitcoin Miners (DryRun) in region eu-central-1...
2022-11-09 04:16:28,793 - INFO - RunInstances API Call
2022-11-09 04:16:30,346 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:30,355 - INFO - Spinning up Bitcoin Miners (DryRun) in region ap-northeast-2...
2022-11-09 04:16:30,356 - INFO - RunInstances API Call
2022-11-09 04:16:30,706 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:30,719 - INFO - Spinning up Bitcoin Miners (DryRun) in region ap-south-1...
2022-11-09 04:16:30,720 - INFO - RunInstances API Call
2022-11-09 04:16:31,475 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:31,517 - INFO - Spinning up Bitcoin Miners (DryRun) in region ap-southeast-2...
2022-11-09 04:16:31,518 - INFO - RunInstances API Call
2022-11-09 04:16:32,524 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:32,533 - INFO - Spinning up Bitcoin Miners (DryRun) in region eu-west-2...
2022-11-09 04:16:32,533 - INFO - RunInstances API Call
2022-11-09 04:16:33,621 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:33,630 - INFO - Spinning up Bitcoin Miners (DryRun) in region eu-north-1...
2022-11-09 04:16:33,630 - INFO - RunInstances API Call
2022-11-09 04:16:34,853 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.
2022-11-09 04:16:34,862 - INFO - Spinning up Bitcoin Miners (DryRun) in region ap-southeast-1...
2022-11-09 04:16:34,862 - INFO - RunInstances API Call
2022-11-09 04:16:35,644 - ERROR - An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
2022-11-09 04:16:35,654 - INFO - Spinning up Bitcoin Miners (DryRun) in region ap-northeast-3...
2022-11-09 04:16:35,654 - INFO - RunInstances API Call
2022-11-09 04:16:35,872 - ERROR - An error occurred (UnsupportedOperation) when calling the RunInstances operation: Tagging an elastic gpu on create is not yet supported in this region.

エラーが出まくっていますが正常な動作です。 ec2:RunInstances で EC2 インスタンスを各リージョンに作成していますが、DruRun フラグを付けているので実際に作成されることはありません。

また、カレントディレクトリにログが出力されます。内容は上記のコマンドの出力と同じです。

対処してみた

先ほど実行したシナリオ mining-bitcoin を検知できるようにします。GuardDuty では検知できませんでした。(DryRun だから?)

シナリオの内容

以下のコマンドで mining-bitcoin のシナリオ内容を表示できます。

$ cloudsaga --about mining-bitcoin

        Bitcoin Mining Scenario:
        This scenario simulates the creation of Bitcoin mining instances.
        Attackers attempt to create Bitcoin mining instances using Amazon EC2,
        in order to leverage legitimate AWS customer's resources for their own purposes.

        Resources Checked:
        Amazon EC2

攻撃者がマイニング用に EC2 インスタンスを立ち上げるといった内容です。

ソースコード を見るに、具体的な処理としては

  1. 各リージョンで ec2:DescribeInstances を実行して、EC2 インスタンスが存在しないリージョンを特定する。
  2. 1 で特定したリージョンにハイパフォーマンスな EC2 インスタンスを作成する。

といった流れです。攻撃者は、攻撃対象が普段使っていないリージョンに EC2 を作成することで発覚を遅らせます。

なお、このシナリオでは EC2 をデフォルト VPC に作成するため、デフォルト VPC を削除していた場合は実行できません。 デフォルト VPC を再作成する必要があります。

構築

普段使っているリージョン以外のリージョンで EC2 インスタンスを立ち上げたら運用者にメールで通知する仕組みを構築します。 今回は東京リージョンを普段使いしている想定で、それ以外のリージョンで EC2 インスタンスを起動されたら通知を行います。

構成図は以下の通りです。

メール通知するまでの流れとしては

  1. 管理イベントを収集する CloudTrail の証跡ログを CloudWatch Logs でモニタリングする。
  2. 普段使いのリージョン以外のリージョンで ec2:RunInstances が実行されたら CloudWatch Alarm で作成したアラームがアラーム状態になる。
  3. アラーム状態になったとき SNS トピックに通知し、トピックをサブスクライブしていたメールアドレスに通知が届く。

これを構築する CDK コードは以下になっています。20行目で設定したメールアドレスにメールが届きます。 また、設定直後に指定したメールアドレス宛に確認メールが届くので Confirm subscription をクリックしてサブスクリプションによる通知を許可します。

import * as cdk from "aws-cdk-lib"
import * as s3 from "aws-cdk-lib/aws-s3"
import * as sns from "aws-cdk-lib/aws-sns"
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions'
import * as cloudtrail from "aws-cdk-lib/aws-cloudtrail"
import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch"
import * as cw_actions from 'aws-cdk-lib/aws-cloudwatch-actions'
import * as logs from "aws-cdk-lib/aws-logs"
import { Construct } from 'constructs'

export class CloudSagaTestStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    // EC2 インスタンスの作成を許可するリージョン
    const excludedRegions = ['ap-northeast-1']

    // 異常時にメール送信するための SNSトピック
    const topic = new sns.Topic(this, 'Topic')
    topic.addSubscription(new subscriptions.EmailSubscription('hogehoge@example.com'))

    // CloudTrail ログの送信先となる CloudWatch ロググループ
    const logGroup = new logs.LogGroup(this, 'Logs', {
      retention: logs.RetentionDays.ONE_DAY,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    })
    // 想定外のリージョンで RunInstances が実行されたことを検知するためのメトリクス
    const metricFilter = new logs.MetricFilter(this, 'MetricFilter', {
      logGroup,
      metricNamespace: 'CloudSagaTest',
      metricName: 'UnexpectedRunInstancesActionHasOccurred',
      filterPattern: logs.FilterPattern.all(
        logs.FilterPattern.stringValue('$.eventSource', '=', 'ec2.amazonaws.com'),
        logs.FilterPattern.stringValue('$.eventName', '=', 'RunInstances'),
        ...excludedRegions.map((region) => logs.FilterPattern.stringValue('$.awsRegion', '!=', region))
      ),
      metricValue: "1",
    })
    // ↑のメトリクスが 1 になったときに発されるアラーム
    const alarm = new cloudwatch.Alarm(this, 'Alarm', {
      metric: metricFilter.metric(),
      threshold: 1,
      evaluationPeriods: 1,
      treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
    })
    alarm.addAlarmAction(new cw_actions.SnsAction(topic))

    // CloudTrail 証跡
    new cloudtrail.Trail(this, 'Trail', {
      sendToCloudWatchLogs: true,
      cloudWatchLogGroup: logGroup,
      isMultiRegionTrail: true,
    })
  }
}

(疑似)インシデント発生

CloudShell で次のコマンドを実行して、疑似インシデントを発生させます。

$ cloudsaga --scenario mining-bitcoin

数分後にメールが届きます。検知できました。やったね!

調査

検知ができたので、次は調査です。(実際のインシデントでは調査の前に応急処置を行ったりしますが、今回はスキップ)

アラームの内容からどこかのリージョンで EC2 インスタンスの起動が行われたことが分かっているので、証跡ログから ec2:RunInstances が実行されたことを確認します。 この構成では証跡ログを CloudWatch Logs でキャプチャしているため、CloudWatch Logs Insight を使って調査します。

東京リージョン以外で実行された ec2:RunInstances を知りたいので、以下のクエリを実行します。

fields @timestamp, @message
| filter (eventSource = "ec2.amazonaws.com" and eventName = "RunInstances" and awsRegion not in ["ap-northeast-1"])
| sort @timestamp desc

結果が得られました。これにより、より詳細な情報を得ることができます。 実際のインシデントであれば、使われたアカウントなどの調査へと繋がっていきますが、今回はここで終わりとします。

他の対処法

今回は CloudWatch Logs を使いましたが、低コストで済ませたいなら CloudTrail のログを S3 に保存して日次バッチで Amazon Athena を使って分析する、といった方法も取れると思います。

今回は DryRun ですが、EC2 が実際に作成されていた場合は AWS Resource Explorer を使って他リージョンで作成された EC2 を探すのもよいかもしれません。

まとめ

AWS CloudSaga を使って疑似的にセキュリティイベントを発生させました。 また、そのセキュリティイベントを検知できるような仕組みの構築をしました。

今回は mining-bitcoin のシナリオを使いましたが、他にもシナリオはあります。 皆さんの AWS 環境でセキュリティイベントを検知できるか試してみてはいかがでしょうか。