Suspicious AWS console logins by credential access alerts
Id | b51fe620-62ad-4ed2-9d40-5c97c0a8231f |
Rulename | Suspicious AWS console logins by credential access alerts |
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. |
Severity | Medium |
Tactics | InitialAccess CredentialAccess |
Techniques | T1078 |
Required data connectors | AWS AzureActiveDirectoryIdentityProtection BehaviorAnalytics MicrosoftDefenderAdvancedThreatProtection MicrosoftThreatProtection OfficeATP |
Kind | Scheduled |
Query frequency | 1d |
Query period | 1d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi Cloud Attack Coverage Essentials - Resource Abuse/Analytic Rules/SuspiciousAWSConsolLoginByCredentialAceessAlerts.yaml |
Version | 1.0.1 |
Arm template | b51fe620-62ad-4ed2-9d40-5c97c0a8231f.json |
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))
queryFrequency: 1d
relevantTechniques:
- T1078
name: Suspicious AWS console logins by credential access alerts
queryPeriod: 1d
kind: Scheduled
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))
triggerOperator: gt
customDetails:
AzureUserUPN: AccountUPN
UserAgent: UserAgent
AWSUSerUPN: CTUPN
ComonIp: SourceIpAddress
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Multi Cloud Attack Coverage Essentials - Resource Abuse/Analytic Rules/SuspiciousAWSConsolLoginByCredentialAceessAlerts.yaml
triggerThreshold: 0
entityMappings:
- fieldMappings:
- identifier: FullName
columnName: AccountUPN
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
entityType: Account
- fieldMappings:
- identifier: Address
columnName: SourceIpAddress
entityType: IP
tactics:
- InitialAccess
- CredentialAccess
id: b51fe620-62ad-4ed2-9d40-5c97c0a8231f
severity: Medium
requiredDataConnectors:
- dataTypes:
- SecurityAlert
connectorId: OfficeATP
- 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
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.'
{
"$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 Cloud Attack Coverage Essentials - Resource Abuse/Analytic Rules/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"
}
]
}