Insider Risk_High User Security Incidents Correlation
Id | 28a75d10-9b75-4192-9863-e452c3ad24db |
Rulename | Insider Risk_High User Security Incidents Correlation |
Description | This alert joins SecurityAlerts to SecurityIncidents to associate Security Alerts and Incidents with user accounts. This aligns all Microsoft Alerting Products with Microsoft Incident Generating Products (Microsoft Sentinel, M365 Defender) for a count of user security incidents over time. The default threshold is 5 security incidents, and this is customizable per the organization’s requirements. Results include UserPrincipalName (UPN), SecurityIncident, LastIncident, ProductName, LastObservedTime, and Previous Incidents. There is an option for configuration of correlations against Microsoft Sentinel watchlists. For more information, see Investigate incidents with Microsoft Sentinel. |
Severity | High |
Tactics | Execution |
Techniques | T1204 |
Required data connectors | AzureActiveDirectoryIdentityProtection AzureSecurityCenter IoT MicrosoftCloudAppSecurity MicrosoftDefenderAdvancedThreatProtection OfficeATP |
Kind | Scheduled |
Query frequency | 7d |
Query period | 7d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/MicrosoftPurviewInsiderRiskManagement/Analytic Rules/InsiderRiskHighUserIncidentsCorrelation.yaml |
Version | 1.1.1 |
Arm template | 28a75d10-9b75-4192-9863-e452c3ad24db.json |
let PreviousIncidents =
SecurityIncident
| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber
| mv-expand AlertIds
| extend AlertId = tostring(AlertIds)
| join kind= innerunique (
SecurityAlert
)
on $left.AlertId == $right.SystemAlertId
| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId
| mv-expand todynamic(Entities)
| where Entities["Type"] =~ "account"
| extend Name = tostring(tolower(Entities["Name"])), NTDomain = tostring(Entities["NTDomain"]), UPNSuffix = tostring(Entities["UPNSuffix"]), AadUserId = tostring(Entities["AadUserId"]), AadTenantId = tostring(Entities["AadTenantId"]),
Sid = tostring(Entities["Sid"]), IsDomainJoined = tobool(Entities["IsDomainJoined"]), Host = tostring(Entities["Host"])
| extend UPN = iff(Name != "" and UPNSuffix != "", strcat(Name, "@", UPNSuffix), "")
| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)
| where UPN <> ""
| summarize PreviousIncidents = make_set(IncidentNumber) by UPN;
let LastTimeObserved =
SecurityIncident
| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber
| mv-expand AlertIds
| extend AlertId = tostring(AlertIds)
| join kind= innerunique (
SecurityAlert
)
on $left.AlertId == $right.SystemAlertId
| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId
| mv-expand todynamic(Entities)
| where Entities["Type"] =~ "account"
| extend Name = tostring(tolower(Entities["Name"])), NTDomain = tostring(Entities["NTDomain"]), UPNSuffix = tostring(Entities["UPNSuffix"]), AadUserId = tostring(Entities["AadUserId"]), AadTenantId = tostring(Entities["AadTenantId"]),
Sid = tostring(Entities["Sid"]), IsDomainJoined = tobool(Entities["IsDomainJoined"]), Host = tostring(Entities["Host"])
| extend UPN = iff(Name != "" and UPNSuffix != "", strcat(Name, "@", UPNSuffix), "")
| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)
| where UPN <> ""
| summarize arg_max(TimeGenerated, IncidentName) by UPN;
SecurityIncident
| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber
| mv-expand AlertIds
| extend AlertId = tostring(AlertIds)
| join kind= innerunique (
SecurityAlert
)
on $left.AlertId == $right.SystemAlertId
| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId
| mv-expand todynamic(Entities)
| where Entities["Type"] =~ "account"
| extend Name = tostring(tolower(Entities["Name"])), NTDomain = tostring(Entities["NTDomain"]), UPNSuffix = tostring(Entities["UPNSuffix"]), AadUserId = tostring(Entities["AadUserId"]), AadTenantId = tostring(Entities["AadTenantId"]),
Sid = tostring(Entities["Sid"]), IsDomainJoined = tobool(Entities["IsDomainJoined"]), Host = tostring(Entities["Host"])
| extend UPN = iff(Name != "" and UPNSuffix != "", strcat(Name, "@", UPNSuffix), "")
| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)
| where UPN <> ""
| summarize count() by UPN, IncidentName, IncidentNumber, IncidentUrl, Severity, ProductName
| extend SecurityIncidents = count_
| where SecurityIncidents > 5 //Adjust & Tune within Organzational Requirements
| join (LastTimeObserved) on UPN
| project-rename LastObserved = TimeGenerated, LastIncident = IncidentNumber
| project-away IncidentName, count_, UPN1, IncidentName1, Severity, IncidentUrl
| join kind=inner (PreviousIncidents) on UPN
// | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UPN == $right.SearchKey
| project UPN, SecurityIncidents, LastIncident, ProductName, LastObserved, PreviousIncidents
| sort by SecurityIncidents desc
| extend AccountCustomEntity = UPN
severity: High
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/MicrosoftPurviewInsiderRiskManagement/Analytic Rules/InsiderRiskHighUserIncidentsCorrelation.yaml
description: |
'This alert joins SecurityAlerts to SecurityIncidents to associate Security Alerts and Incidents with user accounts. This aligns all Microsoft Alerting Products with Microsoft Incident Generating Products (Microsoft Sentinel, M365 Defender) for a count of user security incidents over time. The default threshold is 5 security incidents, and this is customizable per the organization's requirements. Results include UserPrincipalName (UPN), SecurityIncident, LastIncident, ProductName, LastObservedTime, and Previous Incidents. There is an option for configuration of correlations against Microsoft Sentinel watchlists. For more information, see [Investigate incidents with Microsoft Sentinel]( https://docs.microsoft.com/azure/sentinel/investigate-cases).'
triggerOperator: gt
queryPeriod: 7d
requiredDataConnectors:
- dataTypes:
- SecurityAlert (MDATP)
connectorId: MicrosoftDefenderAdvancedThreatProtection
- dataTypes:
- SecurityAlert (IPC)
connectorId: AzureActiveDirectoryIdentityProtection
- dataTypes:
- SecurityAlert (ASC)
connectorId: AzureSecurityCenter
- dataTypes:
- SecurityAlert (ASC for IoT)
connectorId: IoT
- dataTypes:
- SecurityAlert (ASC for IoT)
connectorId: MicrosoftCloudAppSecurity
- dataTypes:
- SecurityAlert (MCAS)
connectorId: IoT
- dataTypes:
- SecurityAlert (Office 365)
connectorId: OfficeATP
incidentConfiguration:
createIncident: true
groupingConfiguration:
lookbackDuration: 3d
matchingMethod: Selected
enabled: true
groupByEntities:
- Account
reopenClosedIncident: true
queryFrequency: 7d
triggerThreshold: 0
tactics:
- Execution
query: |
let PreviousIncidents =
SecurityIncident
| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber
| mv-expand AlertIds
| extend AlertId = tostring(AlertIds)
| join kind= innerunique (
SecurityAlert
)
on $left.AlertId == $right.SystemAlertId
| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId
| mv-expand todynamic(Entities)
| where Entities["Type"] =~ "account"
| extend Name = tostring(tolower(Entities["Name"])), NTDomain = tostring(Entities["NTDomain"]), UPNSuffix = tostring(Entities["UPNSuffix"]), AadUserId = tostring(Entities["AadUserId"]), AadTenantId = tostring(Entities["AadTenantId"]),
Sid = tostring(Entities["Sid"]), IsDomainJoined = tobool(Entities["IsDomainJoined"]), Host = tostring(Entities["Host"])
| extend UPN = iff(Name != "" and UPNSuffix != "", strcat(Name, "@", UPNSuffix), "")
| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)
| where UPN <> ""
| summarize PreviousIncidents = make_set(IncidentNumber) by UPN;
let LastTimeObserved =
SecurityIncident
| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber
| mv-expand AlertIds
| extend AlertId = tostring(AlertIds)
| join kind= innerunique (
SecurityAlert
)
on $left.AlertId == $right.SystemAlertId
| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId
| mv-expand todynamic(Entities)
| where Entities["Type"] =~ "account"
| extend Name = tostring(tolower(Entities["Name"])), NTDomain = tostring(Entities["NTDomain"]), UPNSuffix = tostring(Entities["UPNSuffix"]), AadUserId = tostring(Entities["AadUserId"]), AadTenantId = tostring(Entities["AadTenantId"]),
Sid = tostring(Entities["Sid"]), IsDomainJoined = tobool(Entities["IsDomainJoined"]), Host = tostring(Entities["Host"])
| extend UPN = iff(Name != "" and UPNSuffix != "", strcat(Name, "@", UPNSuffix), "")
| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)
| where UPN <> ""
| summarize arg_max(TimeGenerated, IncidentName) by UPN;
SecurityIncident
| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber
| mv-expand AlertIds
| extend AlertId = tostring(AlertIds)
| join kind= innerunique (
SecurityAlert
)
on $left.AlertId == $right.SystemAlertId
| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId
| mv-expand todynamic(Entities)
| where Entities["Type"] =~ "account"
| extend Name = tostring(tolower(Entities["Name"])), NTDomain = tostring(Entities["NTDomain"]), UPNSuffix = tostring(Entities["UPNSuffix"]), AadUserId = tostring(Entities["AadUserId"]), AadTenantId = tostring(Entities["AadTenantId"]),
Sid = tostring(Entities["Sid"]), IsDomainJoined = tobool(Entities["IsDomainJoined"]), Host = tostring(Entities["Host"])
| extend UPN = iff(Name != "" and UPNSuffix != "", strcat(Name, "@", UPNSuffix), "")
| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)
| where UPN <> ""
| summarize count() by UPN, IncidentName, IncidentNumber, IncidentUrl, Severity, ProductName
| extend SecurityIncidents = count_
| where SecurityIncidents > 5 //Adjust & Tune within Organzational Requirements
| join (LastTimeObserved) on UPN
| project-rename LastObserved = TimeGenerated, LastIncident = IncidentNumber
| project-away IncidentName, count_, UPN1, IncidentName1, Severity, IncidentUrl
| join kind=inner (PreviousIncidents) on UPN
// | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UPN == $right.SearchKey
| project UPN, SecurityIncidents, LastIncident, ProductName, LastObserved, PreviousIncidents
| sort by SecurityIncidents desc
| extend AccountCustomEntity = UPN
kind: Scheduled
relevantTechniques:
- T1204
version: 1.1.1
id: 28a75d10-9b75-4192-9863-e452c3ad24db
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- fieldMappings:
- columnName: AccountCustomEntity
identifier: AadUserId
entityType: Account
name: Insider Risk_High User Security Incidents Correlation
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/28a75d10-9b75-4192-9863-e452c3ad24db')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/28a75d10-9b75-4192-9863-e452c3ad24db')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "Scheduled",
"apiVersion": "2022-11-01-preview",
"properties": {
"displayName": "Insider Risk_High User Security Incidents Correlation",
"description": "'This alert joins SecurityAlerts to SecurityIncidents to associate Security Alerts and Incidents with user accounts. This aligns all Microsoft Alerting Products with Microsoft Incident Generating Products (Microsoft Sentinel, M365 Defender) for a count of user security incidents over time. The default threshold is 5 security incidents, and this is customizable per the organization's requirements. Results include UserPrincipalName (UPN), SecurityIncident, LastIncident, ProductName, LastObservedTime, and Previous Incidents. There is an option for configuration of correlations against Microsoft Sentinel watchlists. For more information, see [Investigate incidents with Microsoft Sentinel]( https://docs.microsoft.com/azure/sentinel/investigate-cases).'\n",
"severity": "High",
"enabled": true,
"query": "let PreviousIncidents =\nSecurityIncident\n| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber\n| mv-expand AlertIds\n| extend AlertId = tostring(AlertIds)\n| join kind= innerunique ( \n SecurityAlert \n )\n on $left.AlertId == $right.SystemAlertId\n| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId\n| mv-expand todynamic(Entities)\n| where Entities[\"Type\"] =~ \"account\"\n| extend Name = tostring(tolower(Entities[\"Name\"])), NTDomain = tostring(Entities[\"NTDomain\"]), UPNSuffix = tostring(Entities[\"UPNSuffix\"]), AadUserId = tostring(Entities[\"AadUserId\"]), AadTenantId = tostring(Entities[\"AadTenantId\"]), \n Sid = tostring(Entities[\"Sid\"]), IsDomainJoined = tobool(Entities[\"IsDomainJoined\"]), Host = tostring(Entities[\"Host\"])\n| extend UPN = iff(Name != \"\" and UPNSuffix != \"\", strcat(Name, \"@\", UPNSuffix), \"\")\n| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)\n| where UPN <> \"\"\n| summarize PreviousIncidents = make_set(IncidentNumber) by UPN;\nlet LastTimeObserved =\nSecurityIncident\n| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber\n| mv-expand AlertIds\n| extend AlertId = tostring(AlertIds)\n| join kind= innerunique ( \n SecurityAlert \n )\n on $left.AlertId == $right.SystemAlertId\n| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId\n| mv-expand todynamic(Entities)\n| where Entities[\"Type\"] =~ \"account\"\n| extend Name = tostring(tolower(Entities[\"Name\"])), NTDomain = tostring(Entities[\"NTDomain\"]), UPNSuffix = tostring(Entities[\"UPNSuffix\"]), AadUserId = tostring(Entities[\"AadUserId\"]), AadTenantId = tostring(Entities[\"AadTenantId\"]), \n Sid = tostring(Entities[\"Sid\"]), IsDomainJoined = tobool(Entities[\"IsDomainJoined\"]), Host = tostring(Entities[\"Host\"])\n| extend UPN = iff(Name != \"\" and UPNSuffix != \"\", strcat(Name, \"@\", UPNSuffix), \"\")\n| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)\n| where UPN <> \"\"\n| summarize arg_max(TimeGenerated, IncidentName) by UPN;\n SecurityIncident\n| summarize hint.strategy = shuffle arg_max(LastModifiedTime, *) by IncidentNumber\n| mv-expand AlertIds\n| extend AlertId = tostring(AlertIds)\n| join kind= innerunique ( \n SecurityAlert \n )\n on $left.AlertId == $right.SystemAlertId\n| summarize hint.strategy = shuffle arg_max(TimeGenerated, *), NumberOfUpdates = count() by SystemAlertId\n| mv-expand todynamic(Entities)\n| where Entities[\"Type\"] =~ \"account\"\n| extend Name = tostring(tolower(Entities[\"Name\"])), NTDomain = tostring(Entities[\"NTDomain\"]), UPNSuffix = tostring(Entities[\"UPNSuffix\"]), AadUserId = tostring(Entities[\"AadUserId\"]), AadTenantId = tostring(Entities[\"AadTenantId\"]), \n Sid = tostring(Entities[\"Sid\"]), IsDomainJoined = tobool(Entities[\"IsDomainJoined\"]), Host = tostring(Entities[\"Host\"])\n| extend UPN = iff(Name != \"\" and UPNSuffix != \"\", strcat(Name, \"@\", UPNSuffix), \"\")\n| extend Href_ = tostring(parse_json(ExtendedLinks)[0].Href)\n| where UPN <> \"\"\n| summarize count() by UPN, IncidentName, IncidentNumber, IncidentUrl, Severity, ProductName\n| extend SecurityIncidents = count_\n| where SecurityIncidents > 5 //Adjust & Tune within Organzational Requirements\n| join (LastTimeObserved) on UPN\n| project-rename LastObserved = TimeGenerated, LastIncident = IncidentNumber\n| project-away IncidentName, count_, UPN1, IncidentName1, Severity, IncidentUrl\n| join kind=inner (PreviousIncidents) on UPN\n// | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UPN == $right.SearchKey\n| project UPN, SecurityIncidents, LastIncident, ProductName, LastObserved, PreviousIncidents\n| sort by SecurityIncidents desc\n| extend AccountCustomEntity = UPN\n",
"queryFrequency": "P7D",
"queryPeriod": "P7D",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Execution"
],
"techniques": [
"T1204"
],
"alertRuleTemplateName": "28a75d10-9b75-4192-9863-e452c3ad24db",
"incidentConfiguration": {
"createIncident": true,
"groupingConfiguration": {
"enabled": true,
"groupByEntities": [
"Account"
],
"reopenClosedIncident": true,
"matchingMethod": "Selected",
"lookbackDuration": "P3D"
}
},
"eventGroupingSettings": {
"aggregationKind": "SingleAlert"
},
"customDetails": null,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "AccountCustomEntity",
"identifier": "AadUserId"
}
]
}
],
"templateVersion": "1.1.1",
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/MicrosoftPurviewInsiderRiskManagement/Analytic Rules/InsiderRiskHighUserIncidentsCorrelation.yaml"
}
}
]
}