Amazon announced GuardDuty during the AWS re:invent 2017 to protect AWS accounts against a wide variety of attacks.

Amazon GuardDuty is a threat detection service that continuously monitors malicious activity and unauthorized behavior to protect your Amazon Web Services accounts, workloads, and data stored in Amazon S3. While using the cloud simplifies the collection and aggregation of account and network activity, ongoing analysis of event log data for potential threats can be time-consuming for security teams. With GuardDuty, you now have an intelligent, cost-effective option for continuous threat detection in the Amazon Web Services Cloud. The service uses machine learning, anomaly detection, and built-in threat intelligence to identify and prioritize potential threats. GuardDuty analyzes hundreds of billions of events from multiple Amazon Web Services data sources, such as CloudTrail event logs, Amazon VPC flow logs, and DNS logs. You can deploy or enable GuardDuty directly from the Amazon Web Services management console. Integration with Amazon CloudWatch Events makes GuardDuty findings usable, easy to aggregate across multiple accounts, and easy to integrate with existing workflow and event management systems. https://www.amazonaws.cn/en/guardduty/

In several scenarios, I have seen that GuarDuty logs are neither persisted nor acted on. In the following setup, we will have Amazon EventBridge Rules persist the data in Amazon CloudWatch log groups and send notifications via Amazon Simple Notification Service (SNS) for high severity vulnerabilities identified by GuardDuty.

Table of Contents
📧 Create SNS Topic GuardDutyNotification
💾 Create CloudWatch log group /aws/events/GuardDuty
🔔 Create EventBridge rules
🧪 Testing the setup
🧰 CloudFormation template

📧 Create SNS Topic GuardDutyNotification

We want to retrieve all GuardDuty findings via email to a shared mailbox, so our security team can act on high-severity findings. Therefore we open the AWS console and navigate to SNS and create a standard topic.

AWS console to create SNS topic
AWS console to create SNS topic

The SNS topic will be invoked by GuardDuty. We need to make sure that the principal of events.amazonaws.com is allowed to invoke the GuardDutyNotification SNS topic. Additionally, we need to allow the sns:Publish action on the same. Please check that the attached access policy is set up correctly and allows those actions. Otherwise, the events will not be delivered to your inbox. It is very hard to debug that later as we can only use CloudTrail and won't see any logs in CloudWatch. While preparing the CloudFormation template for this setup, I missed some parts of the policy and it took me a good hour to identify why I wasn't receiving any email notifications.

The below policy also includes a mitigation for the confused deputy problem, as we are using the AWS:SourceOwner condition. Always make sure to add those conditions to your policies, as misconfigurations are one of the highest risks of cloud environments. You can find more information on this problem here: https://docs.aws.amazon.com/systems-manager/latest/userguide/cross-service-confused-deputy-prevention.html

SNS topic access policy
SNS topic access policy
{
  "Version": "2008-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": "arn:aws:sns:<region>:<accountID>:GuardDutyNotification",
      "Condition": {
        "StringEquals": {
          "AWS:SourceOwner": "<accountID>"
        }
      }
    },
    {
      "Sid": "AWSEvents_GuardDutyNotification",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:<region>:<accountID>:GuardDutyNotification",
    }
  ]
}

SNS topic access policy (please modify the region and account ID in the JSON file)

Afterward, we can create a subscription for our newly created GuardDutyNotification SNS topic to receive notifications for our high severity findings. I recommend using a shared mailbox email address. After confirming the email address we can go ahead with the further setup.

💾 Create CloudWatch log group /aws/events/GuardDuty

We want to be notified via the cloud-native EventBridge alerting rule sets.
Therefore we need to send our GuardDuty findings to a CloudWatch log group. In CloudWatch we create a new log group with the name /aws/events/GuardDuty.

AWS CloudWatch log group
AWS CloudWatch log group

🔔 Create EventBridge rules

We will create two rules in EventBridge:

GuardDutyEventDeliveryRule —> Deliver GuadDuty logs to CloudWatch
GuardDutyEventNotificationRule —> Send notifications to the SNS topic

Create GuardDutyEventDeliveryRule

For both rules that we will create, we use the default event bus and choose rule with an event pattern. In the event pattern, we can define exactly what conditions need to be met so that the rule will be triggered.

Rule details

Firstly we will use AWS service for the event source. As we want to persist all GuardDuty findings that are generated, we choose GuardDuty for the AWS service. This way we will consume all GuardDuty findings in this rule. With the event type GuardDuty Finding, we will limit the events to findings from GuardDuty only.

Event pattern

The event pattern should look like this:

{
  "source": ["aws.guardduty"],
  "detail-type": ["GuardDuty Finding"]
}

Then we need to choose the target that the rule should invoke. For our GuardDutyEventDeliveryRule, we choose the created CloudWatch log group as Target 1. This way we persist all GuardDuty findings in CloudWatch and are not bound to the default finding retention period of 90 days. Additionally, we can directly analyze the data in CloudWatch.

AWS EventBridge target configuration
AWS EventBridge target configuration

Create GuardDutyEventNotificationRule

Afterward, we create the second rule in EventBridge and name it GuardDutyEventNotificationRule. As the second rule invokes the SNS topic to send emails to the subscribed email address, we need to adjust the event pattern to only match findings from GuardDuty with a severity of 7 and higher. Otherwise, we will flood our inbox with a lot of low severity and priority findings. Findings with a severity of 7 are considered high severity based on the CVSS v3.0 ratings. You can find more details here: https://nvd.nist.gov/vuln-metrics/cvss. CVSS defines the following severity types:

Severity Base Score
None 0
Low 0.1-3.9
Medium 4.0-6.9
High 7.0-8.9
Critical 9.0-10.0

Currently, AWS only uses severity values within the 0.1 to 8.9 range. Values 0 and 9.0 to 10.0 are currently reserved for future use.

To enable this setup we need to choose Custom patterns (JSON editor) in the event pattern section and insert the following JSON information:

{
  "detail-type": ["GuardDuty Finding"],
  "source": ["aws.guardduty"],
  "detail": {
    "severity": [7, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10, 10.0]
  }
}

This will automatically extract the severity of the finding and match it against the list of values defined in the list.
If you want to test the Event pattern you may get the error “Sample event did not match the event pattern”, this is because the severity of the sample event is lower than what we define in the Event pattern. We can ignore the error message and continue our setup.

In the last step, we choose the created SNS topic as Target 1. As we don't want to receive the full JSON GuardDuty finding via email, we can adjust the information that is sent via an input transformer. You can find this option in the additional settings section.

AWS EventBridge target configuration
AWS EventBridge target configuration

In the Target input transformer section, we can define variables that reference values from the original event source. We can use those variables later in our template.
I recommend the following Input Path, which should cover the most important information from the GuardDuty finding:

{
  "account": "$.account",
  "arn": "$.detail.arn",
  "description": "$.detail.description",
  "findingID": "$.detail.id",
  "firstSeen": "$.detail.service.eventFirstSeen",
  "region": "$.region",
  "severity": "$.detail.severity",
  "title": "$.detail.title",
  "type": "$.detail.type"
}

Next, we can define the content of the email in the template section.
I recommend having the basic finding information in there and adding a link to the AWS console for more information about the finding:

"You have a severity <severity> GuardDuty finding type <type>"
"Region: <region>"
"Account: <account>"
"First seen: <firstSeen>"
"ARN: <arn>"
"Finding Description:"
"<description>"
"Link to GuardDuty console: https://console.aws.amazon.com/guardduty/home?region=<region>#/findings?search=id%3D<findingID>"

You can use the variables that we have defined in the Input Path for your custom email template via angle brackets (<variableName>). Make sure to use quotation marks to encapsulate each line otherwise, AWS won't be able to save the template. If you have an error in your template you will see the following error message:

Invalid InputTemplate for target GuardDutyEventNotificationRuleBus

Lastly, we save the rule and we are done with the complete setup and can continue with testing.

Join our community of cloud security professionals. 🔐

Subscribe to our newsletter

🧪 Testing the setup

We can use the Generate sample findings option in GuardDuty to test the function. This will create 50 GuardDuty findings.

You can use sample findings to help visualize the different finding types that GuardDuty generates. Sample findings can also be used to test notifications or automation that you have configured for findings. Select Generate sample findings to populate your current findings list with one sample finding for each finding type.
The title of sample findings always begins with [SAMPLE] in the console. Additionally, sample findings have a value of "sample": true in the additionalInfo section of the finding JSON details.
https://docs.aws.amazon.com/guardduty/latest/ug/sample_findings.html

If you only want to create a single finding, you can also use the AWS CLI.

root@alexanderhose:~$ aws guardduty create-sample-findings --detector-id <detectorID> --finding-types Backdoor:EC2/DenialOfService.Tcp

To get the detector ID, you need to navigate to the settings tab, which is on the left-hand side of the GuardDuty console. There you can find your regional detector ID, which you can then insert in the command.

🧰 CloudFormation template

For an easy setup of the above architecture, you can use the following CloudFormation template. It will automatically provision all services and configurations.

This setup is a regional setup, which means that the CloudFormation template only creates the setup in the current region. Deploy the template in all desired regions.

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Template to centralize AWS GuardDuty logging and enable cloud native alerting on vulnerabilities",
    "Parameters": {
        "EmailAddress": {
            "Type": "String",
            "Description": "Email Address",
            "Default": "[email protected]"
        }
    },
    "Resources": {
        "GuardDutySNSTopic": {
            "Type": "AWS::SNS::Topic",
            "Properties": {
                "Subscription": [
                    {
                        "Endpoint": {
                            "Ref": "EmailAddress"
                        },
                        "Protocol": "email"
                    }
                ],
                "TopicName": "GuardDutyNotification"
            }
        },
        "GuardDutySNSTopicPolicy": {
            "Type": "AWS::SNS::TopicPolicy",
            "Properties": {
                "PolicyDocument": {
                    "Version": "2008-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": {
                                "Fn::Join": [
                                    "",
                                    [
                                        "arn:aws:sns:",
                                        {
                                            "Ref": "AWS::Region"
                                        },
                                        ":",
                                        {
                                            "Ref": "AWS::AccountId"
                                        },
                                        ":GuardDutyNotification"
                                    ]
                                ]
                            },
                            "Condition": {
                                "StringEquals": {
                                    "AWS:SourceOwner": {
                                        "Ref": "AWS::AccountId"
                                    }
                                }
                            }
                        },
                        {
                            "Sid": "AWSEvents_GuardDutyNotification",
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "events.amazonaws.com"
                            },
                            "Action": "sns:Publish",
                            "Resource": {
                                "Fn::Join": [
                                    "",
                                    [
                                        "arn:aws:sns:",
                                        {
                                            "Ref": "AWS::Region"
                                        },
                                        ":",
                                        {
                                            "Ref": "AWS::AccountId"
                                        },
                                        ":GuardDutyNotification"
                                    ]
                                ]
                            }
                        }
                    ]
                },
                "Topics": [
                    {
                        "Ref": "GuardDutySNSTopic"
                    }
                ]
            }
        },
        "GuardDutyLogGroup": {
            "Type": "AWS::Logs::LogGroup",
            "Properties": {
                "LogGroupName": "/aws/events/GuardDuty"
            }
        },
        "GuardDutyEventDeliveryRule": {
            "Type": "AWS::Events::Rule",
            "Properties": {
                "State": "ENABLED",
                "Name": "GuardDutyEventDeliveryRule",
                "EventPattern": {
                    "source": [
                        "aws.guardduty"
                    ],
                    "detail-type": [
                        "GuardDuty Finding"
                    ]
                },
                "Targets": [
                    {
                        "Arn": {
                            "Fn::GetAtt": [
                                "GuardDutyLogGroup",
                                "Arn"
                            ]
                        },
                        "Id": "GuardDutyEventDeliveryRuleBus"
                    }
                ]
            },
            "DependsOn": [
                "GuardDutyLogGroup"
            ]
        },
        "GuardDutyEventNotificationRule": {
            "Type": "AWS::Events::Rule",
            "Properties": {
                "State": "ENABLED",
                "Name": "GuardDutyEventNotificationRule",
                "EventPattern": {
                    "source": [
                        "aws.guardduty"
                    ],
                    "detail-type": [
                        "GuardDuty Finding"
                    ],
                    "detail": {
                        "severity": [
                            7,
                            7.0,
                            7.1,
                            7.2,
                            7.3,
                            7.4,
                            7.5,
                            7.6,
                            7.7,
                            7.8,
                            7.9,
                            8,
                            8.0,
                            8.1,
                            8.2,
                            8.3,
                            8.4,
                            8.5,
                            8.6,
                            8.7,
                            8.8,
                            8.9,
                            9,
                            9.0,
                            9.1,
                            9.2,
                            9.3,
                            9.4,
                            9.5,
                            9.6,
                            9.7,
                            9.8,
                            9.9,
                            10,
                            10.0
                        ]
                    }
                },
                "Targets": [
                    {
                        "Arn": {
                            "Ref": "GuardDutySNSTopic"
                        },
                        "Id": "GuardDutyEventNotificationRuleBus",
                        "InputTransformer": {
                            "InputPathsMap": {
                                "severity": "$.detail.severity",
                                "findingID": "$.detail.id",
                                "type": "$.detail.type",
                                "firstSeen": "$.detail.service.eventFirstSeen",
                                "account": "$.account",
                                "title": "$.detail.title",
                                "region": "$.region",
                                "description": "$.detail.description",
                                "arn": "$.detail.arn"
                            },
                            "InputTemplate": "\"You have a severity <severity> GuardDuty finding type <type>\"\n\"Region: <region>\"\n\"Account: <account>\"\n\"First seen: <firstSeen>\"\n\"ARN: <arn>\"\n\"Finding Description:\"\n\"<description>\"\n\"Link to GuardDuty console: https://console.aws.amazon.com/guardduty/home?region=<region>#/findings?search=id%3D<findingID>\""
                        }
                    }
                ]
            },
            "DependsOn": [
                "GuardDutySNSTopic"
            ]
        }
    },
    "Outputs": {
        "GuardDutySNSTopic": {
            "Description": "SNS Topic created",
            "Value": {
                "Ref": "GuardDutySNSTopic"
            }
        },
        "GuardDutyLogGroup": {
            "Description": "Log Group created",
            "Value": {
                "Ref": "GuardDutyLogGroup"
            }
        },
        "GuardDutyEventDeliveryRule": {
            "Description": "Event Delivery Rule Topic created",
            "Value": {
                "Ref": "GuardDutyEventDeliveryRule"
            }
        },
        "GuardDutyEventNotificationRule": {
            "Description": "Event Notification Rule Topic created",
            "Value": {
                "Ref": "GuardDutyEventNotificationRule"
            }
        }
    }
}

GuardDuty logging and alerting CloudFormation template in JSON

Share this post