Query Details

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 profile picture

Fabian Bader

Released: September 23, 2023

Tables

AWSGuardDuty

Keywords

AWSGuardDutyingestion_timeagoTimeGeneratedfindingTokensThreatPurposeResourceTypeAffectedThreatFamilyNameDetectionMechanismArtifactSeverityAccessKeyDetailsRdsDbUserDetailsKubernetesDetailsServiceActionRemoteIpAddressLocalIpAddressRemoteAWSAccountIdAccountUpnAccountNameUPNSuffixGuardDutyDetailsFindingLinkRegionIdServiceDetailsEbsVolumeScanDetailsthreatNamesfilePathsDirectoryFileHashesNameCurrentRowNumberMalwareInformationFindingArnFindingIdAWSAccountIdActivityTypePartitionSchemaVersionTimeCreatedTypeProductNameProviderNameCloudApplicationIdIsSamplesuppressionEnabledcreateIncidentgroupingConfigurationreopenClosedIncidentlookbackDurationmatchingMethodgroupByEntitiesgroupByAlertDetailsgroupByCustomDetailsaggregationKindalertDisplayNameFormatalertDescriptionFormatalertTacticsColumnNamealertSeverityColumnNamealertDynamicPropertiesMalwareEntitiessuppressionDuration

Operators

whereextendsplitcasecoalescetostringparse_jsonisnotemptydynamiciffbag_packstrcatjoinmv-expandsortrow_numbermake_setproject-awayproject-renamesuppressionEnabledcreateIncidentenabledreopenClosedIncidentlookbackDurationmatchingMethodgroupByEntitiesaggregationKindalertDisplayNameFormatalertDescriptionFormatalertTacticsColumnNamealertSeverityColumnNamealertDynamicPropertiesentityTypefieldMappingsidentifiercolumnName

Severity

Medium

Frequency: 1h

Period: 2h

Actions

GitHub