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

Insider Risk_High User Security Alert Correlations

Back
Ida4fb4255-f55b-4c24-b396-976ee075d406
RulenameInsider Risk_High User Security Alert Correlations
DescriptionThis alert joins SecurityAlerts from Microsoft Products with SecurityIncidents from Microsoft Sentinel and Microsoft 365 Defender. This join allows for identifying patterns in user principal names associated with respective security alerts. A machine learning function (Basket) is leveraged with a .001 threshold. Baset finds all frequent patterns of discrete attributes (dimensions) in the data. It returns the frequent patterns passed the frequency threshold. This query evaluates UserPrincipalName for patterns in SecurityAlerts and Reporting Security Tools. This query can be further tuned/configured for higher confidence percentages, security products, or alert severities pending the needs of the organization. There is an option for configuration of correlations against Microsoft Sentinel watchlists. For more information on the basket plugin, see basket plugin
SeverityMedium
TacticsExecution
TechniquesT1204
Required data connectorsAzureActiveDirectoryIdentityProtection
AzureSecurityCenter
IoT
MicrosoftCloudAppSecurity
MicrosoftDefenderAdvancedThreatProtection
OfficeATP
KindScheduled
Query frequency7d
Query period7d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/MicrosoftPurviewInsiderRiskManagement/Analytic Rules/InsiderRiskHighUserAlertsCorrelation.yaml
Version1.1.1
Arm templatea4fb4255-f55b-4c24-b396-976ee075d406.json
Deploy To Azure
let AlertLinks = SecurityAlert
| 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 AlertLinks=make_set(AlertLink) 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 <> ""
| project UPN, AlertName, Severity, ProductName, TimeGenerated | summarize arg_max(AlertName, Severity, ProductName, TimeGenerated) 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 <> ""
| project UPN, AlertName, Severity, ProductName
| evaluate basket(0.001)
| where UPN <> ""
// | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UPN == $right.SearchKey
| project UPN, Percent, AlertName, Severity, ProductName
| join (LastTimeObserved) on UPN
| sort by Percent desc
| extend LastObserved = TimeGenerated
| join kind=inner (AlertLinks) on UPN
| project UPN, AlertName, Severity, ProductName, Percent, LastObserved, AlertLinks
| extend AccountCustomEntity = UPN
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/MicrosoftPurviewInsiderRiskManagement/Analytic Rules/InsiderRiskHighUserAlertsCorrelation.yaml
severity: Medium
name: Insider Risk_High User Security Alert Correlations
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: AccountCustomEntity
    identifier: AadUserId
relevantTechniques:
- T1204
queryFrequency: 7d
triggerThreshold: 0
queryPeriod: 7d
description: |
    'This alert joins SecurityAlerts from Microsoft Products with SecurityIncidents from Microsoft Sentinel and Microsoft 365 Defender. This join allows for identifying patterns in user principal names associated with respective security alerts. A machine learning function (Basket) is leveraged with a .001 threshold. Baset finds all frequent patterns of discrete attributes (dimensions) in the data. It returns the frequent patterns passed the frequency threshold. This query evaluates UserPrincipalName for patterns in SecurityAlerts and Reporting Security Tools. This query can be further tuned/configured for higher confidence percentages, security products, or alert severities pending the needs of the organization. There is an option for configuration of correlations against Microsoft Sentinel watchlists. For more information on the basket plugin, see [basket plugin](https://docs.microsoft.com/azure/data-explorer/kusto/query/basketplugin)'
id: a4fb4255-f55b-4c24-b396-976ee075d406
version: 1.1.1
tactics:
- Execution
query: |
  let AlertLinks = SecurityAlert
  | 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 AlertLinks=make_set(AlertLink) 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 <> ""
  | project UPN, AlertName, Severity, ProductName, TimeGenerated | summarize arg_max(AlertName, Severity, ProductName, TimeGenerated) 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 <> ""
  | project UPN, AlertName, Severity, ProductName
  | evaluate basket(0.001)
  | where UPN <> ""
  // | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UPN == $right.SearchKey
  | project UPN, Percent, AlertName, Severity, ProductName
  | join (LastTimeObserved) on UPN
  | sort by Percent desc
  | extend LastObserved = TimeGenerated
  | join kind=inner (AlertLinks) on UPN
  | project UPN, AlertName, Severity, ProductName, Percent, LastObserved, AlertLinks
  | extend AccountCustomEntity = UPN  
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
triggerOperator: gt
{
  "$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/a4fb4255-f55b-4c24-b396-976ee075d406')]",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/a4fb4255-f55b-4c24-b396-976ee075d406')]",
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
      "kind": "Scheduled",
      "apiVersion": "2022-11-01-preview",
      "properties": {
        "displayName": "Insider Risk_High User Security Alert Correlations",
        "description": "'This alert joins SecurityAlerts from Microsoft Products with SecurityIncidents from Microsoft Sentinel and Microsoft 365 Defender. This join allows for identifying patterns in user principal names associated with respective security alerts. A machine learning function (Basket) is leveraged with a .001 threshold. Baset finds all frequent patterns of discrete attributes (dimensions) in the data. It returns the frequent patterns passed the frequency threshold. This query evaluates UserPrincipalName for patterns in SecurityAlerts and Reporting Security Tools. This query can be further tuned/configured for higher confidence percentages, security products, or alert severities pending the needs of the organization. There is an option for configuration of correlations against Microsoft Sentinel watchlists. For more information on the basket plugin, see [basket plugin](https://docs.microsoft.com/azure/data-explorer/kusto/query/basketplugin)'\n",
        "severity": "Medium",
        "enabled": true,
        "query": "let AlertLinks = SecurityAlert\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 AlertLinks=make_set(AlertLink) by UPN;\nlet LastTimeObserved = 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| project UPN, AlertName, Severity, ProductName, TimeGenerated | summarize arg_max(AlertName, Severity, ProductName, TimeGenerated) by UPN;\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| project UPN, AlertName, Severity, ProductName\n| evaluate basket(0.001)\n| where UPN <> \"\"\n// | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UPN == $right.SearchKey\n| project UPN, Percent, AlertName, Severity, ProductName\n| join (LastTimeObserved) on UPN\n| sort by Percent desc\n| extend LastObserved = TimeGenerated\n| join kind=inner (AlertLinks) on UPN\n| project UPN, AlertName, Severity, ProductName, Percent, LastObserved, AlertLinks\n| extend AccountCustomEntity = UPN\n",
        "queryFrequency": "P7D",
        "queryPeriod": "P7D",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0,
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Execution"
        ],
        "techniques": [
          "T1204"
        ],
        "alertRuleTemplateName": "a4fb4255-f55b-4c24-b396-976ee075d406",
        "customDetails": null,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "identifier": "AadUserId",
                "columnName": "AccountCustomEntity"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/MicrosoftPurviewInsiderRiskManagement/Analytic Rules/InsiderRiskHighUserAlertsCorrelation.yaml",
        "templateVersion": "1.1.1"
      }
    }
  ]
}