Back
Idb51fe620-62ad-4ed2-9d40-5c97c0a8231f
RulenameSuspicious AWS console logins by credential access alerts
DescriptionThis query aims to detect instances of successful AWS console logins that align with high-severity credential access or Initial Access alerts generated by Defender Products.

Specifically, it focuses on scenarios where the successful login takes place within a 60-minute timeframe of the high-severity alert. The login is considered relevant if it originates from an IP address associated with potential attackers.
SeverityMedium
TacticsInitialAccess
CredentialAccess
TechniquesT1078
Required data connectorsAWS
AzureActiveDirectoryIdentityProtection
BehaviorAnalytics
MicrosoftDefenderAdvancedThreatProtection
MicrosoftThreatProtection
OfficeATP
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi%20Cloud%20Attack%20Coverage%20Essentials%20-%20Resource%20Abuse/Analytic%20Rules/SuspiciousAWSConsolLoginByCredentialAceessAlerts.yaml
Version1.0.1
Arm templateb51fe620-62ad-4ed2-9d40-5c97c0a8231f.json
Deploy To Azure
SecurityAlert 
// Filtering alerts based on Microsoft product names
  | where ProductName in ("Microsoft 365 Defender", "Azure Active Directory", "Microsoft Defender Advanced Threat Protection", "Microsoft Cloud App Security","Azure Active Directory Identity Protection", "Microsoft Defender ATP")
// Narrowing down alerts to specific tactics
  | where Tactics in("CredentialAccess", "InitialAccess")
// Focusing on high-severity alerts
  | where AlertSeverity == "High"
// Parsing and extending the 'Entities' column as JSON objects
  | extend Entities = parse_json(Entities) 
// Exploring IP entities within the alert entities
  | mv-apply Entity = Entities on 
      ( 
      where Entity.Type == 'ip' 
      | extend EntityIp = tostring(Entity.Address) 
      ) 
// Exploring account entities within the alert entities
  | mv-apply Entity = Entities on 
      ( 
      where Entity.Type == 'account' 
      | extend AccountObjectId = tostring(Entity.AadUserId)
      )
// Filtering out alerts with missing IP or account information
  | where isnotempty(EntityIp) and isnotempty(AccountObjectId)
// Summarizing relevant fields for further analysis
  | summarize 
      by 
      AlertName,
      ProductName,
      ProviderName,
      AlertSeverity,
      EntityIp,
      Tactics,
      Techniques,
      AlertTime= bin(TimeGenerated, 1min),
      AccountObjectId,
      AlertTimeGenerated=TimeGenerated
// Joining with IdentityInfo to obtain additional account details
  | join kind=inner (
      IdentityInfo
      | where TimeGenerated >= ago(1d)
      | distinct AccountObjectId, AccountUPN=tolower(AccountUPN)
      )
      on AccountObjectId 
      |extend Name = tostring(split(AccountUPN,'@')[0]), UPNSuffix =tostring(split(AccountUPN,'@')[1])
// Joining with AWSCloudTrail data to correlate AWS console logins
  | join kind=inner (
      AWSCloudTrail
      | where EventName == "ConsoleLogin"
      | extend CTUPN= tolower(tostring(tolower(split(UserIdentityArn, "/", 2)[0])))
      | extend ActionType= tostring(parse_json(ResponseElements).ConsoleLogin)  
      | where ActionType == "Success"
      | extend AWSTime= bin(TimeGenerated, 1min)
      | project
          EventName,
          EventSource,
          EventTypeName,
          RecipientAccountId,
          ResponseElements,
          SessionMfaAuthenticated,
          SourceIpAddress,
          TimeGenerated,
          UserAgent,
          UserIdentityArn,
          UserIdentityType,
          CTUPN,
          AWSTime,
          UserIdentityUserName
      )
      on $left.EntityIp == $right.SourceIpAddress 
// Filtering login event after the Alert generation time
  | where AlertTimeGenerated >= AWSTime
// Calculating the time difference between alert generation and AWS login
  | extend timediff = datetime_diff('minute', AlertTimeGenerated, TimeGenerated) 
// Filtering alerts with a time difference of up to 60 minutes
  | where timediff between ((-60)..(60))
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi%20Cloud%20Attack%20Coverage%20Essentials%20-%20Resource%20Abuse/Analytic%20Rules/SuspiciousAWSConsolLoginByCredentialAceessAlerts.yaml
triggerThreshold: 0
query: |
  SecurityAlert 
  // Filtering alerts based on Microsoft product names
    | where ProductName in ("Microsoft 365 Defender", "Azure Active Directory", "Microsoft Defender Advanced Threat Protection", "Microsoft Cloud App Security","Azure Active Directory Identity Protection", "Microsoft Defender ATP")
  // Narrowing down alerts to specific tactics
    | where Tactics in("CredentialAccess", "InitialAccess")
  // Focusing on high-severity alerts
    | where AlertSeverity == "High"
  // Parsing and extending the 'Entities' column as JSON objects
    | extend Entities = parse_json(Entities) 
  // Exploring IP entities within the alert entities
    | mv-apply Entity = Entities on 
        ( 
        where Entity.Type == 'ip' 
        | extend EntityIp = tostring(Entity.Address) 
        ) 
  // Exploring account entities within the alert entities
    | mv-apply Entity = Entities on 
        ( 
        where Entity.Type == 'account' 
        | extend AccountObjectId = tostring(Entity.AadUserId)
        )
  // Filtering out alerts with missing IP or account information
    | where isnotempty(EntityIp) and isnotempty(AccountObjectId)
  // Summarizing relevant fields for further analysis
    | summarize 
        by 
        AlertName,
        ProductName,
        ProviderName,
        AlertSeverity,
        EntityIp,
        Tactics,
        Techniques,
        AlertTime= bin(TimeGenerated, 1min),
        AccountObjectId,
        AlertTimeGenerated=TimeGenerated
  // Joining with IdentityInfo to obtain additional account details
    | join kind=inner (
        IdentityInfo
        | where TimeGenerated >= ago(1d)
        | distinct AccountObjectId, AccountUPN=tolower(AccountUPN)
        )
        on AccountObjectId 
        |extend Name = tostring(split(AccountUPN,'@')[0]), UPNSuffix =tostring(split(AccountUPN,'@')[1])
  // Joining with AWSCloudTrail data to correlate AWS console logins
    | join kind=inner (
        AWSCloudTrail
        | where EventName == "ConsoleLogin"
        | extend CTUPN= tolower(tostring(tolower(split(UserIdentityArn, "/", 2)[0])))
        | extend ActionType= tostring(parse_json(ResponseElements).ConsoleLogin)  
        | where ActionType == "Success"
        | extend AWSTime= bin(TimeGenerated, 1min)
        | project
            EventName,
            EventSource,
            EventTypeName,
            RecipientAccountId,
            ResponseElements,
            SessionMfaAuthenticated,
            SourceIpAddress,
            TimeGenerated,
            UserAgent,
            UserIdentityArn,
            UserIdentityType,
            CTUPN,
            AWSTime,
            UserIdentityUserName
        )
        on $left.EntityIp == $right.SourceIpAddress 
  // Filtering login event after the Alert generation time
    | where AlertTimeGenerated >= AWSTime
  // Calculating the time difference between alert generation and AWS login
    | extend timediff = datetime_diff('minute', AlertTimeGenerated, TimeGenerated) 
  // Filtering alerts with a time difference of up to 60 minutes
    | where timediff between ((-60)..(60))
kind: Scheduled
relevantTechniques:
- T1078
version: 1.0.1
tactics:
- InitialAccess
- CredentialAccess
id: b51fe620-62ad-4ed2-9d40-5c97c0a8231f
queryPeriod: 1d
queryFrequency: 1d
severity: Medium
name: Suspicious AWS console logins by credential access alerts
customDetails:
  ComonIp: SourceIpAddress
  AzureUserUPN: AccountUPN
  AWSUSerUPN: CTUPN
  UserAgent: UserAgent
requiredDataConnectors:
- connectorId: OfficeATP
  dataTypes:
  - SecurityAlert
- connectorId: AWS
  dataTypes:
  - AWSCloudTrail
- connectorId: MicrosoftDefenderAdvancedThreatProtection
  dataTypes:
  - SecurityAlert
- connectorId: AzureActiveDirectoryIdentityProtection
  dataTypes:
  - SecurityAlert (IPC)
- connectorId: BehaviorAnalytics
  dataTypes:
  - IdentityInfo
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - SecurityAlert
description: |
  'This query aims to detect instances of successful AWS console logins that align with high-severity credential access or Initial Access alerts generated by Defender Products.
   Specifically, it focuses on scenarios where the successful login takes place within a 60-minute timeframe of the high-severity alert. The login is considered relevant if it originates from an IP address associated with potential attackers.'
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: AccountUPN
  - identifier: Name
    columnName: Name
  - identifier: UPNSuffix
    columnName: UPNSuffix
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SourceIpAddress
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/b51fe620-62ad-4ed2-9d40-5c97c0a8231f')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/b51fe620-62ad-4ed2-9d40-5c97c0a8231f')]",
      "properties": {
        "alertRuleTemplateName": "b51fe620-62ad-4ed2-9d40-5c97c0a8231f",
        "customDetails": {
          "AWSUSerUPN": "CTUPN",
          "AzureUserUPN": "AccountUPN",
          "ComonIp": "SourceIpAddress",
          "UserAgent": "UserAgent"
        },
        "description": "'This query aims to detect instances of successful AWS console logins that align with high-severity credential access or Initial Access alerts generated by Defender Products.\n Specifically, it focuses on scenarios where the successful login takes place within a 60-minute timeframe of the high-severity alert. The login is considered relevant if it originates from an IP address associated with potential attackers.'\n",
        "displayName": "Suspicious AWS console logins by credential access alerts",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountUPN",
                "identifier": "FullName"
              },
              {
                "columnName": "Name",
                "identifier": "Name"
              },
              {
                "columnName": "UPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "SourceIpAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi%20Cloud%20Attack%20Coverage%20Essentials%20-%20Resource%20Abuse/Analytic%20Rules/SuspiciousAWSConsolLoginByCredentialAceessAlerts.yaml",
        "query": "SecurityAlert \n// Filtering alerts based on Microsoft product names\n  | where ProductName in (\"Microsoft 365 Defender\", \"Azure Active Directory\", \"Microsoft Defender Advanced Threat Protection\", \"Microsoft Cloud App Security\",\"Azure Active Directory Identity Protection\", \"Microsoft Defender ATP\")\n// Narrowing down alerts to specific tactics\n  | where Tactics in(\"CredentialAccess\", \"InitialAccess\")\n// Focusing on high-severity alerts\n  | where AlertSeverity == \"High\"\n// Parsing and extending the 'Entities' column as JSON objects\n  | extend Entities = parse_json(Entities) \n// Exploring IP entities within the alert entities\n  | mv-apply Entity = Entities on \n      ( \n      where Entity.Type == 'ip' \n      | extend EntityIp = tostring(Entity.Address) \n      ) \n// Exploring account entities within the alert entities\n  | mv-apply Entity = Entities on \n      ( \n      where Entity.Type == 'account' \n      | extend AccountObjectId = tostring(Entity.AadUserId)\n      )\n// Filtering out alerts with missing IP or account information\n  | where isnotempty(EntityIp) and isnotempty(AccountObjectId)\n// Summarizing relevant fields for further analysis\n  | summarize \n      by \n      AlertName,\n      ProductName,\n      ProviderName,\n      AlertSeverity,\n      EntityIp,\n      Tactics,\n      Techniques,\n      AlertTime= bin(TimeGenerated, 1min),\n      AccountObjectId,\n      AlertTimeGenerated=TimeGenerated\n// Joining with IdentityInfo to obtain additional account details\n  | join kind=inner (\n      IdentityInfo\n      | where TimeGenerated >= ago(1d)\n      | distinct AccountObjectId, AccountUPN=tolower(AccountUPN)\n      )\n      on AccountObjectId \n      |extend Name = tostring(split(AccountUPN,'@')[0]), UPNSuffix =tostring(split(AccountUPN,'@')[1])\n// Joining with AWSCloudTrail data to correlate AWS console logins\n  | join kind=inner (\n      AWSCloudTrail\n      | where EventName == \"ConsoleLogin\"\n      | extend CTUPN= tolower(tostring(tolower(split(UserIdentityArn, \"/\", 2)[0])))\n      | extend ActionType= tostring(parse_json(ResponseElements).ConsoleLogin)  \n      | where ActionType == \"Success\"\n      | extend AWSTime= bin(TimeGenerated, 1min)\n      | project\n          EventName,\n          EventSource,\n          EventTypeName,\n          RecipientAccountId,\n          ResponseElements,\n          SessionMfaAuthenticated,\n          SourceIpAddress,\n          TimeGenerated,\n          UserAgent,\n          UserIdentityArn,\n          UserIdentityType,\n          CTUPN,\n          AWSTime,\n          UserIdentityUserName\n      )\n      on $left.EntityIp == $right.SourceIpAddress \n// Filtering login event after the Alert generation time\n  | where AlertTimeGenerated >= AWSTime\n// Calculating the time difference between alert generation and AWS login\n  | extend timediff = datetime_diff('minute', AlertTimeGenerated, TimeGenerated) \n// Filtering alerts with a time difference of up to 60 minutes\n  | where timediff between ((-60)..(60))\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess",
          "InitialAccess"
        ],
        "techniques": [
          "T1078"
        ],
        "templateVersion": "1.0.1",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}