AWS Guard Duty Alert
Query
// https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html
AWSGuardDuty
| where ingestion_time() > ago(1h)
| extend TimeGenerated = TimeCreated
// Parse the finding
// https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-format.html
// Example: "ThreatPurpose:ResourceTypeAffected/ThreatFamilyName.DetectionMechanism!Artifact"
| extend findingTokens = split(ActivityType, ":")
| extend ThreatPurpose=findingTokens[0]
| extend findingTokens=split(findingTokens[1], "/")
| extend ResourceTypeAffected=findingTokens[0]
| extend findingTokens= split(findingTokens[1], ".")
| extend ThreatFamilyName=findingTokens[0]
| extend findingTokens=split(findingTokens[1], "!")
| extend DetectionMechanism=findingTokens[0], Artifact=findingTokens[1]
// Assign severity level
// https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html#guardduty_findings-severity
| extend Severity =
case (
Severity >= 7.0,
"High",
Severity between (4.0 .. 6.9),
"Medium",
Severity between (1.0 .. 3.9),
"Low",
"Unknown"
)
// Pull out any available resource details we can extract entities from. These may not exist in the alert.
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_Resource.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_AccessKeyDetails.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_RdsDbUserDetails.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_KubernetesDetails.html
| extend AccessKeyDetails=ResourceDetails.accessKeyDetails
| extend RdsDbUserDetails=ResourceDetails.rdsDbUserDetails
| extend KubernetesDetails=ResourceDetails.kubernetesDetails
// Pull out any available action details we can extract entities from. These may not exist in the alert.
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_Action.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_AwsApiCallAction.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_KubernetesApiCallAction.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_NetworkConnectionAction.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_RdsLoginAttemptAction.html
| extend ServiceAction =
case(
isnotempty(ServiceDetails.action.awsApiCallAction),
ServiceDetails.action.awsApiCallAction,
isnotempty(ServiceDetails.action.kubernetesApiCallAction),
ServiceDetails.action.kubernetesApiCallAction,
isnotempty(ServiceDetails.action.networkConnectionAction),
ServiceDetails.action.networkConnectionAction,
isnotempty(ServiceDetails.action.rdsLoginAttemptAction),
ServiceDetails.action.rdsLoginAttemptAction,
dynamic(null)
)
// The IPv4 remote address of the connection
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_RemoteIpDetails.html
// or
// The IP of the Kubernetes API caller and the IPs of any proxies or load balancers between the caller and the API endpoint
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_KubernetesApiCallAction.html
| extend RemoteIpAddress =
coalesce(
tostring(ServiceAction.remoteIpDetails.ipAddressV4),
tostring(parse_json(ServiceAction.sourceIPs)[0])
)
// The IPv4 local address of the connection
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_LocalIpDetails.html
| extend LocalIpAddress = ServiceAction.localIpDetails.ipAddressV4
// The AWS account ID of the remote API caller.
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_AwsApiCallAction.html
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_RemoteAccountDetails.html
| extend RemoteAWSAccountId = ServiceAction.remoteAccountDetails.accountId
// The IAM access key details (user information) of a user that engaged in the activity that prompted GuardDuty to generate a finding
// https://docs.aws.amazon.com/guardduty/latest/APIReference/API_AccessKeyDetails.html
| extend AccountUpn =
case(
AccessKeyDetails.userType == "IAMUser",
AccessKeyDetails.userName,
AccessKeyDetails.userType == "AssumedRole",
split(AccessKeyDetails.principalId, ":", 1)[0],
isnotempty(RdsDbUserDetails.user),
RdsDbUserDetails.user,
isnotempty(KubernetesDetails.kubernetesUserDetails.username),
KubernetesDetails.kubernetesUserDetails.username,
""
)
| extend AccountName = split(AccountUpn, "@", 0)[0]
| extend UPNSuffix = split(AccountUpn, "@", 1)[0]
// Clean up the output
| extend GuardDutyDetails =
bag_pack(
"DetectorId",
ServiceDetails.detectorId,
"Partition",
Partition,
"Region",
Region
)
| extend FindingLink =
iff(
isnotempty(Region) and isnotempty(Id),
strcat("https://", Region, ".console.aws.amazon.com/guardduty/home?region=", Region, "#/findings?fId=", Id),
""
)
// Add additional entity information if malware was found
| join kind=leftouter (
AWSGuardDuty
| where isnotempty(ServiceDetails.ebsVolumeScanDetails.scanDetections.highestSeverityThreatDetails.threatName)
| extend threatNames = ServiceDetails.ebsVolumeScanDetails.scanDetections.threatDetectedByName.threatNames
| mv-expand threatNames
| mv-expand threatNames.filePaths
| extend Directory = threatNames_filePaths.filePath
| extend FileHashes = threatNames_filePaths.hash
| extend Name = threatNames_filePaths.fileName
| sort by Id
| extend CurrentRowNumber=row_number(2, prev(Id) != Id)
| extend MalwareInformation = bag_pack(@"$id", CurrentRowNumber, "Asset", false, "Type", "file", "Directory", Directory, "Name", Name)
| project Id, MalwareInformation
| summarize MalwareEntities = make_set(MalwareInformation) by Id
)
on Id
| project-away *1
| project-rename
FindingArn=Arn,
FindingId=Id,
AWSAccountId=AccountId
| project-away
ActivityType,
findingTokens,
Partition,
Region,
SchemaVersion,
TimeCreated,
Type
| extend ProductName = 'AWS Guard Duty'
| extend ProviderName = 'AWS'
| extend CloudApplicationId = '11599'
| extend IsSample = iff(ServiceDetails.additionalInfo.['sample'] == "true", true, false)Explanation
This query is used to create an alert for each finding in Amazon GuardDuty, a threat detection service for AWS accounts. The query filters the findings based on their ingestion time and parses the finding details. It assigns severity levels to the findings and extracts resource and action details. It also extracts entity information related to malware findings. The query then cleans up the output and maps the entities to specific columns. The alert configuration includes incident creation and grouping settings, as well as alert details override and custom details.
Details

Fabian Bader
Released: September 23, 2023
Tables
AWSGuardDuty
Keywords
AWSGuardDutyingestion_timeagoTimeGeneratedfindingTokensThreatPurposeResourceTypeAffectedThreatFamilyNameDetectionMechanismArtifactSeverityAccessKeyDetailsRdsDbUserDetailsKubernetesDetailsServiceActionRemoteIpAddressLocalIpAddressRemoteAWSAccountIdAccountUpnAccountNameUPNSuffixGuardDutyDetailsFindingLinkRegionIdServiceDetailsEbsVolumeScanDetailsthreatNamesfilePathsDirectoryFileHashesNameCurrentRowNumberMalwareInformationFindingArnFindingIdAWSAccountIdActivityTypePartitionSchemaVersionTimeCreatedTypeProductNameProviderNameCloudApplicationIdIsSamplesuppressionEnabledcreateIncidentgroupingConfigurationreopenClosedIncidentlookbackDurationmatchingMethodgroupByEntitiesgroupByAlertDetailsgroupByCustomDetailsaggregationKindalertDisplayNameFormatalertDescriptionFormatalertTacticsColumnNamealertSeverityColumnNamealertDynamicPropertiesMalwareEntitiessuppressionDuration
Operators
whereextendsplitcasecoalescetostringparse_jsonisnotemptydynamiciffbag_packstrcatjoinmv-expandsortrow_numbermake_setproject-awayproject-renamesuppressionEnabledcreateIncidentenabledreopenClosedIncidentlookbackDurationmatchingMethodgroupByEntitiesaggregationKindalertDisplayNameFormatalertDescriptionFormatalertTacticsColumnNamealertSeverityColumnNamealertDynamicPropertiesentityTypefieldMappingsidentifiercolumnName
Severity
MediumFrequency: 1h
Period: 2h