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

Dataverse - Anomalous application user activity

Back
Id0820da12-e895-417f-9175-7c256fcfb33e
RulenameDataverse - Anomalous application user activity
DescriptionIdentifies anomalies in activity patterns of Dataverse application (non-interactive) users, based on activity falling outside the normal pattern of use.
SeverityMedium
TacticsCredentialAccess
Execution
Persistence
TechniquesT1528
T1569
T0871
T0834
T0859
Required data connectorsDataverse
KindScheduled
Query frequency5h
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Anomalous application user activity.yaml
Version3.2.0
Arm template0820da12-e895-417f-9175-7c256fcfb33e.json
Deploy To Azure
let query_lookback = 14d;
let query_frequency = 5h;
let anomaly_threshold = 2.5;
let seasonality = -1;
let trend = 'linefit';
let step_duration = 5h;
let app_user_regex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\.com$";
let guid_regex = "([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})";
let application_users = DataverseActivity
    | where TimeGenerated >= ago(query_frequency)
    | where UserId !endswith "@onmicrosoft.com" and UserId != "Unknown"
    | summarize by UserId
    | where split(UserId, "@")[1] matches regex app_user_regex;
DataverseActivity
| where TimeGenerated >= startofday(ago(query_lookback))
| where UserId in (application_users)
| where isnotempty(OriginalObjectId)
| make-series TotalEvents = count() default=0 on TimeGenerated from startofday(ago(query_lookback)) to now() step step_duration by UserId, InstanceUrl, OriginalObjectId
| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(TotalEvents, anomaly_threshold, seasonality, trend)
| mv-expand
    TotalEvents to typeof(double),
    AnomalyTimeGenerated = TimeGenerated to typeof(datetime),
    Anomalies to typeof(double),
    Score to typeof(double),
    Baseline to typeof(long)
| where Anomalies > 0
| extend Details = bag_pack(
                       "TotalEvents",
                       TotalEvents,
                       "Anomalies",
                       Anomalies,
                       "Baseline",
                       Baseline,
                       "Score",
                       Score,
                       "OriginalObjectId",
                       OriginalObjectId
                   )
| summarize Details = make_set(Details, 100) by UserId, InstanceUrl, AnomalyTimeGenerated
| extend
    CloudAppId = int(32780),
    AadUserId = extract(guid_regex, 1, tostring(split(UserId, "@")[0]))
| project
    AnomalyTimeGenerated,
    UserId,
    AadUserId,
    InstanceUrl,
    Details,
    CloudAppId
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: AadUserId
    columnName: AadUserId
- entityType: CloudApplication
  fieldMappings:
  - identifier: AppId
    columnName: CloudAppId
  - identifier: InstanceName
    columnName: InstanceUrl
queryFrequency: 5h
name: Dataverse - Anomalous application user activity
alertDetailsOverride:
  alertDisplayNameFormat: 'Dataverse - Non-interactive account anomaly detected in {{InstanceUrl}} '
  alertDescriptionFormat: 'Anomaly detected on {{UserId}} in {{InstanceUrl}}.  Details: {{Details}}'
kind: Scheduled
tactics:
- CredentialAccess
- Execution
- Persistence
triggerThreshold: 0
query: |
  let query_lookback = 14d;
  let query_frequency = 5h;
  let anomaly_threshold = 2.5;
  let seasonality = -1;
  let trend = 'linefit';
  let step_duration = 5h;
  let app_user_regex = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\.com$";
  let guid_regex = "([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})";
  let application_users = DataverseActivity
      | where TimeGenerated >= ago(query_frequency)
      | where UserId !endswith "@onmicrosoft.com" and UserId != "Unknown"
      | summarize by UserId
      | where split(UserId, "@")[1] matches regex app_user_regex;
  DataverseActivity
  | where TimeGenerated >= startofday(ago(query_lookback))
  | where UserId in (application_users)
  | where isnotempty(OriginalObjectId)
  | make-series TotalEvents = count() default=0 on TimeGenerated from startofday(ago(query_lookback)) to now() step step_duration by UserId, InstanceUrl, OriginalObjectId
  | extend (Anomalies, Score, Baseline) = series_decompose_anomalies(TotalEvents, anomaly_threshold, seasonality, trend)
  | mv-expand
      TotalEvents to typeof(double),
      AnomalyTimeGenerated = TimeGenerated to typeof(datetime),
      Anomalies to typeof(double),
      Score to typeof(double),
      Baseline to typeof(long)
  | where Anomalies > 0
  | extend Details = bag_pack(
                         "TotalEvents",
                         TotalEvents,
                         "Anomalies",
                         Anomalies,
                         "Baseline",
                         Baseline,
                         "Score",
                         Score,
                         "OriginalObjectId",
                         OriginalObjectId
                     )
  | summarize Details = make_set(Details, 100) by UserId, InstanceUrl, AnomalyTimeGenerated
  | extend
      CloudAppId = int(32780),
      AadUserId = extract(guid_regex, 1, tostring(split(UserId, "@")[0]))
  | project
      AnomalyTimeGenerated,
      UserId,
      AadUserId,
      InstanceUrl,
      Details,
      CloudAppId  
relevantTechniques:
- T1528
- T1569
- T0871
- T0834
- T0859
triggerOperator: gt
customDetails:
  InstranceUrl: InstanceUrl
queryPeriod: 14d
eventGroupingSettings:
  aggregationKind: AlertPerResult
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Anomalous application user activity.yaml
severity: Medium
status: Available
id: 0820da12-e895-417f-9175-7c256fcfb33e
requiredDataConnectors:
- connectorId: Dataverse
  dataTypes:
  - DataverseActivity
version: 3.2.0
description: Identifies anomalies in activity patterns of Dataverse application (non-interactive) users, based on activity falling outside the normal pattern of use.
{
  "$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/0820da12-e895-417f-9175-7c256fcfb33e')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/0820da12-e895-417f-9175-7c256fcfb33e')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDescriptionFormat": "Anomaly detected on {{UserId}} in {{InstanceUrl}}.  Details: {{Details}}",
          "alertDisplayNameFormat": "Dataverse - Non-interactive account anomaly detected in {{InstanceUrl}} "
        },
        "alertRuleTemplateName": "0820da12-e895-417f-9175-7c256fcfb33e",
        "customDetails": {
          "InstranceUrl": "InstanceUrl"
        },
        "description": "Identifies anomalies in activity patterns of Dataverse application (non-interactive) users, based on activity falling outside the normal pattern of use.",
        "displayName": "Dataverse - Anomalous application user activity",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AadUserId",
                "identifier": "AadUserId"
              }
            ]
          },
          {
            "entityType": "CloudApplication",
            "fieldMappings": [
              {
                "columnName": "CloudAppId",
                "identifier": "AppId"
              },
              {
                "columnName": "InstanceUrl",
                "identifier": "InstanceName"
              }
            ]
          }
        ],
        "eventGroupingSettings": {
          "aggregationKind": "AlertPerResult"
        },
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Anomalous application user activity.yaml",
        "query": "let query_lookback = 14d;\nlet query_frequency = 5h;\nlet anomaly_threshold = 2.5;\nlet seasonality = -1;\nlet trend = 'linefit';\nlet step_duration = 5h;\nlet app_user_regex = \"^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\\\.com$\";\nlet guid_regex = \"([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})\";\nlet application_users = DataverseActivity\n    | where TimeGenerated >= ago(query_frequency)\n    | where UserId !endswith \"@onmicrosoft.com\" and UserId != \"Unknown\"\n    | summarize by UserId\n    | where split(UserId, \"@\")[1] matches regex app_user_regex;\nDataverseActivity\n| where TimeGenerated >= startofday(ago(query_lookback))\n| where UserId in (application_users)\n| where isnotempty(OriginalObjectId)\n| make-series TotalEvents = count() default=0 on TimeGenerated from startofday(ago(query_lookback)) to now() step step_duration by UserId, InstanceUrl, OriginalObjectId\n| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(TotalEvents, anomaly_threshold, seasonality, trend)\n| mv-expand\n    TotalEvents to typeof(double),\n    AnomalyTimeGenerated = TimeGenerated to typeof(datetime),\n    Anomalies to typeof(double),\n    Score to typeof(double),\n    Baseline to typeof(long)\n| where Anomalies > 0\n| extend Details = bag_pack(\n                       \"TotalEvents\",\n                       TotalEvents,\n                       \"Anomalies\",\n                       Anomalies,\n                       \"Baseline\",\n                       Baseline,\n                       \"Score\",\n                       Score,\n                       \"OriginalObjectId\",\n                       OriginalObjectId\n                   )\n| summarize Details = make_set(Details, 100) by UserId, InstanceUrl, AnomalyTimeGenerated\n| extend\n    CloudAppId = int(32780),\n    AadUserId = extract(guid_regex, 1, tostring(split(UserId, \"@\")[0]))\n| project\n    AnomalyTimeGenerated,\n    UserId,\n    AadUserId,\n    InstanceUrl,\n    Details,\n    CloudAppId\n",
        "queryFrequency": "PT5H",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess",
          "Execution",
          "Persistence"
        ],
        "techniques": [
          "T1528",
          "T1569"
        ],
        "templateVersion": "3.2.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}