Amazon GuardDuty Malware Protection for Amazon S3でマルウェア検知後にメールで通知してみた

Amazon GuardDuty Malware Protection for Amazon S3でマルウェア検知後にメールで通知してみた

Clock Icon2024.12.19

Amazon GuardDuty Malware Protection for Amazon S3でマルウェア検知のテストを行う機会があったのでブログに残します。

やること

以下の構成を作成してS3バケット内にEICAR テストファイルをアップロードした際にAmazon SNS経由でメール通知されることを確認します。
GuardDuty_20241219

Amazon GuardDuty Malware Protection for Amazon S3って何?という方は以下のブログをご一読ください。
この機能を有効化すると対象のS3バケットにアップロードされたオブジェクトに対してスキャンを行いマルウェアなど悪意のあるファイルを見つけることができる機能です。
https://dev.classmethod.jp/articles/release-guardduty-s3-malware-protection/

リソース作成

リソースの作成は以下のCloudFormationテンプレートで行いました。

AWSTemplateFormatVersion: "2010-09-09"

Description: GuardDuty Stack

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  MailAddress:
    Type: String

Resources:
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------# 
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub s3-malware-test-${AWS::AccountId}-${AWS::Region}
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256

# ------------------------------------------------------------#
# SNS
# ------------------------------------------------------------# 
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties: 
      Subscription:
        - Endpoint: !Ref MailAddress
          Protocol: email
      TopicName: sns-malware-test

  SNSTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties: 
      PolicyDocument:
        Version: '2012-10-17'
        Id: __default_policy_ID
        Statement:
          - Sid: __default_statement_ID
            Effect: Allow
            Principal: 
              AWS: '*'
            Action: 
              - 'SNS:GetTopicAttributes'
              - 'SNS:SetTopicAttributes'
              - 'SNS:AddPermission'
              - 'SNS:RemovePermission'
              - 'SNS:DeleteTopic'
              - 'SNS:Subscribe'
              - 'SNS:ListSubscriptionsByTopic'
              - 'SNS:Publish'
            Resource: !Ref SNSTopic
            Condition: 
              StringEquals: 
                'AWS:SourceOwner': !Sub ${AWS::AccountId}
          - Sid: 'EventBridge Use'
            Effect: Allow
            Principal: 
              Service: events.amazonaws.com
            Action: 'sns:Publish'
            Resource: !Ref SNSTopic
      Topics:
        - !Ref SNSTopic

# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------# 
  GuardDutyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "role-guardduty-malware-test"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Condition:
            StringEquals:
              aws:SourceAccount: !Sub ${AWS::AccountId}
          Action: "sts:AssumeRole"
          Effect: "Allow"
          Principal:
            Service: "malware-protection-plan.guardduty.amazonaws.com"

  GuardDutyPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: policy-guardduty-malware-test
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: "Allow"
          Action: 
            - "events:PutRule"
            - "events:DeleteRule"
            - "events:PutTargets"
            - "events:RemoveTargets"
          Resource: 
            - !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
          Condition: 
            StringLike: 
              events:ManagedBy: "malware-protection-plan.guardduty.amazonaws.com"
        - Effect: "Allow"
          Action: 
            - "events:DescribeRule"
            - "events:ListTargetsByRule"
          Resource: 
            - !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
        - Effect: "Allow"
          Action: 
            - "s3:PutObjectTagging"
            - "s3:GetObjectTagging"
            - "s3:PutObjectVersionTagging"
            - "s3:GetObjectVersionTagging"
          Resource: 
            - !Sub "${S3Bucket.Arn}/*"
        - Effect: "Allow"
          Action: 
            - "s3:PutBucketNotification"
            - "s3:GetBucketNotification"
          Resource: 
            - !GetAtt S3Bucket.Arn
        - Effect: "Allow"
          Action: 
            - "s3:PutObject"
          Resource: 
            - !Sub "${S3Bucket.Arn}/malware-protection-resource-validation-object"
        - Effect: "Allow"
          Action: 
            - "s3:ListBucket"
          Resource: 
            - !GetAtt S3Bucket.Arn
        - Effect: "Allow"
          Action: 
            - "s3:GetObject"
            - "s3:GetObjectVersion"
          Resource: 
            - !Sub "${S3Bucket.Arn}/*"
      Roles:
        - !Ref GuardDutyRole

# ------------------------------------------------------------#
# GuardDuty
# ------------------------------------------------------------# 
  GuardDuty:
      DependsOn: GuardDutyPolicy
      Type: AWS::GuardDuty::MalwareProtectionPlan
      Properties:
          ProtectedResource:
              S3Bucket:
                  BucketName: !Ref S3Bucket
          Role: !GetAtt  GuardDutyRole.Arn

# ------------------------------------------------------------#
# EventBridge
# ------------------------------------------------------------# 
  EventBridgeRule:
      Type: AWS::Events::Rule
      Properties:
          Name: rule-guardduty-malware-protection
          EventPattern:
              source:
                - aws.guardduty
              detail-type:
                - GuardDuty Malware Protection Object Scan Result
              detail:
                scanResultDetails:
                  scanResultStatus:
                    - THREATS_FOUND
          Targets:
            - Id: Email
              Arn: !Ref SNSTopic

77行目~147行目でGuardDutyが使用するIAMロールとIAMポリシーを作成しています。
こちらのIAMポリシーは以下のドキュメントに記載されているポリシーを参考にしました。
ドキュメントでは「kms:GenerateDataKey」と「kms:Decrypt」を許可するような形となっていますが、今回作成するS3バケットではSSE-S3を使用しているため、IAMポリシーで許可していなくても使用が可能です。
https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/malware-protection-s3-iam-policy-prerequisite.html
ちなみにIAMロールにS3へのListBucketを許可していないと以下のようなエラーが発生してリソースの作成に失敗します。

The request was rejected because provided IAM role does not have the required permissions to validate S3 bucket ownership.

152行目~159行目でAmazon GuardDuty Malware Protection for Amazon S3を有効化しています。
マルウェア検知時にオブジェクトにタグを付ける設定などもあるのですが、今回は設定せず、シンプルに有効化のみ行っています。
164行目~179行目でEventBridgeルールを作成しています。
マルウェアを検知すると以下のドキュメントに記載されているイベントが発行されます。
https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/monitor-with-eventbridge-s3-malware-protection.html#s3-object-scan-status-malware-protection-s3-ev

{
    "version": "0",
    "id": "72c7d362-737a-6dce-fc78-9e27a0171419",
    "detail-type": "GuardDuty Malware Protection Object Scan Result",
    "source": "aws.guardduty",
    "account": "111122223333",
    "time": "2024-02-28T01:01:01Z",
    "region": "us-east-1",
    "resources": [arn:aws:guardduty:us-east-1:111122223333:malware-protection-plan/b4c7f464ab3a4EXAMPLE],
    "detail": {
        "schemaVersion": "1.0",
        "scanStatus": "COMPLETED",
        "resourceType": "S3_OBJECT",
        "s3ObjectDetails": {
            "bucketName": "amzn-s3-demo-bucket",
            "objectKey": "APKAEIBAERJR2EXAMPLE",
            "eTag": "ASIAI44QH8DHBEXAMPLE",
            "versionId" : "d41d8cd98f00b204e9800998eEXAMPLE",
            "s3Throttled": false
        },
        "scanResultDetails": {
            "scanResultStatus": "THREATS_FOUND",
            "threats": [
                {
                    "name": "EICAR-Test-File (not a virus)"
                }
            ]
        }
    }
}

そのため、EventBridgeルールのイベントパターンでは「scanResultStatus」が「THREATS_FOUND」の時に一致するように以下のようにしています。

{
  "detail-type": ["GuardDuty Malware Protection Object Scan Result"],
  "source": ["aws.guardduty"],
  "detail": {
    "scanResultDetails": {
      "scanResultStatus": ["THREATS_FOUND"]
    }
  }
}

デプロイは以下のAWS CLIコマンドを使用します。

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --parameters ParameterKey=MailAddress,ParameterValue=通知先のメールアドレス --capabilities CAPABILITY_NAMED_IAM

動作確認

リソースの作成が完了したらEICAR テストファイルをダウンロードしてS3へアップロードします。

curl https://secure.eicar.org/eicar.com.txt -o eicar.com.txt
aws s3 cp eicar.com.txt s3://s3-malware-test-アカウントID-ap-northeast-1

正常に設定が完了していると以下のメールが届いていることが確認できます。

{
    "version": "0",
    "id": "11111111-1111-1111-1111-111111111111",
    "detail-type": "GuardDuty Malware Protection Object Scan Result",
    "source": "aws.guardduty",
    "account": "111111111111",
    "time": "2024-12-19T07:52:21Z",
    "region": "ap-northeast-1",
    "resources": [
        "arn:aws:guardduty:ap-northeast-1:111111111111:malware-protection-plan/11111111111111111111"
    ],
    "detail": {
        "schemaVersion": "1.0",
        "scanStatus": "COMPLETED",
        "resourceType": "S3_OBJECT",
        "s3ObjectDetails": {
            "bucketName": "s3-malware-test-111111111111-ap-northeast-1",
            "objectKey": "eicar.com.txt",
            "eTag": "11111111111111111111",
            "versionId": null,
            "s3Throttled": false
        },
        "scanResultDetails": {
            "scanResultStatus": "THREATS_FOUND",
            "threats": [
                {
                    "name": "EICAR-Test-File (not a virus)"
                }
            ]
        }
    }
}

「objectKey」も含まれているので対象ファイルも簡単に検索ができます。

さいごに

Amazon GuardDuty Malware Protection for Amazon S3でマルウェア検知時の通知設定を試してみました。
今回は通知のみの設定ですが、Lambdaなどに連携すればファイルの削除や隔離といったことも可能なはずなので、機会があれば試してみたいと思います。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.