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
customDetails:
  InstranceUrl: InstanceUrl
queryPeriod: 14d
id: 0820da12-e895-417f-9175-7c256fcfb33e
relevantTechniques:
- T1528
- T1569
- T0871
- T0834
- T0859
triggerOperator: gt
entityMappings:
- fieldMappings:
  - columnName: AadUserId
    identifier: AadUserId
  entityType: Account
- fieldMappings:
  - columnName: CloudAppId
    identifier: AppId
  - columnName: InstanceUrl
    identifier: InstanceName
  entityType: CloudApplication
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  
kind: Scheduled
triggerThreshold: 0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Anomalous application user activity.yaml
queryFrequency: 5h
requiredDataConnectors:
- dataTypes:
  - DataverseActivity
  connectorId: Dataverse
eventGroupingSettings:
  aggregationKind: AlertPerResult
name: Dataverse - Anomalous application user activity
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.
alertDetailsOverride:
  alertDisplayNameFormat: 'Dataverse - Non-interactive account anomaly detected in {{InstanceUrl}} '
  alertDescriptionFormat: 'Anomaly detected on {{UserId}} in {{InstanceUrl}}.  Details: {{Details}}'
tactics:
- CredentialAccess
- Execution
- Persistence
severity: Medium
status: Available
{
  "$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"
    }
  ]
}