Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Successful AWS Console Login from IP Address Observed Conducting Password Spray

Back
Id188db479-d50a-4a9c-a041-644bae347d1f
RulenameSuccessful AWS Console Login from IP Address Observed Conducting Password Spray
DescriptionThis query aims to detect instances of successful AWS console login events followed by multiple failed app logons alerts generated by Microsoft Cloud App Security or password spray 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
TechniquesT1110
T1078
Required data connectorsAWS
AzureActiveDirectoryIdentityProtection
BehaviorAnalytics
MicrosoftDefenderAdvancedThreatProtection
MicrosoftThreatProtection
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi Cloud Attack Coverage Essentials - Resource Abuse/Analytic Rules/SuccessfulAWSConsoleLoginfromIPAddressObservedConductingPasswordSpray.yaml
Version1.0.1
Arm template188db479-d50a-4a9c-a041-644bae347d1f.json
Deploy To Azure
SecurityAlert 
// Filtering alerts based on Microsoft product names and Relevent alert names
  | where ProductName in ( "Microsoft Cloud App Security","Azure Active Directory Identity Protection")
  |where AlertName in ("Multiple failed user log on attempts to an app","Password Spray")
// 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 between ((AWSTime - 1h)..(AWSTime + 1h))
// 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 <= 60
queryPeriod: 1d
entityMappings:
- fieldMappings:
  - columnName: AccountUPN
    identifier: FullName
  - columnName: Name
    identifier: Name
  - columnName: UPNSuffix
    identifier: UPNSuffix
  entityType: Account
- fieldMappings:
  - columnName: SourceIpAddress
    identifier: Address
  entityType: IP
id: 188db479-d50a-4a9c-a041-644bae347d1f
name: Successful AWS Console Login from IP Address Observed Conducting Password Spray
description: |
  'This query aims to detect instances of successful AWS console login events followed by multiple failed app logons alerts generated by Microsoft Cloud App Security or password spray 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.'  
tactics:
- InitialAccess
- CredentialAccess
customDetails:
  UserAgent: UserAgent
  AWSUser: UserIdentityArn
  AWSUserUPN: CTUPN
triggerOperator: gt
query: |
  SecurityAlert 
  // Filtering alerts based on Microsoft product names and Relevent alert names
    | where ProductName in ( "Microsoft Cloud App Security","Azure Active Directory Identity Protection")
    |where AlertName in ("Multiple failed user log on attempts to an app","Password Spray")
  // 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 between ((AWSTime - 1h)..(AWSTime + 1h))
  // 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 <= 60  
queryFrequency: 1d
triggerThreshold: 0
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi Cloud Attack Coverage Essentials - Resource Abuse/Analytic Rules/SuccessfulAWSConsoleLoginfromIPAddressObservedConductingPasswordSpray.yaml
requiredDataConnectors:
- dataTypes:
  - AWSCloudTrail
  connectorId: AWS
- dataTypes:
  - SecurityAlert
  connectorId: MicrosoftDefenderAdvancedThreatProtection
- dataTypes:
  - SecurityAlert (IPC)
  connectorId: AzureActiveDirectoryIdentityProtection
- dataTypes:
  - IdentityInfo
  connectorId: BehaviorAnalytics
- dataTypes:
  - SecurityAlert
  connectorId: MicrosoftThreatProtection
version: 1.0.1
relevantTechniques:
- T1110
- T1078
severity: Medium
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2023-02-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/188db479-d50a-4a9c-a041-644bae347d1f')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/188db479-d50a-4a9c-a041-644bae347d1f')]",
      "properties": {
        "alertRuleTemplateName": "188db479-d50a-4a9c-a041-644bae347d1f",
        "customDetails": {
          "AWSUser": "UserIdentityArn",
          "AWSUserUPN": "CTUPN",
          "UserAgent": "UserAgent"
        },
        "description": "'This query aims to detect instances of successful AWS console login events followed by multiple failed app logons alerts generated by Microsoft Cloud App Security or password spray 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. \n The login is considered relevant if it originates from an IP address associated with potential attackers.'\n",
        "displayName": "Successful AWS Console Login from IP Address Observed Conducting Password Spray",
        "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 Cloud Attack Coverage Essentials - Resource Abuse/Analytic Rules/SuccessfulAWSConsoleLoginfromIPAddressObservedConductingPasswordSpray.yaml",
        "query": "SecurityAlert \n// Filtering alerts based on Microsoft product names and Relevent alert names\n  | where ProductName in ( \"Microsoft Cloud App Security\",\"Azure Active Directory Identity Protection\")\n  |where AlertName in (\"Multiple failed user log on attempts to an app\",\"Password Spray\")\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 between ((AWSTime - 1h)..(AWSTime + 1h))\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 <= 60\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "Medium",
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess",
          "InitialAccess"
        ],
        "techniques": [
          "T1078",
          "T1110"
        ],
        "templateVersion": "1.0.1",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}