Dataverse - Terminated employee exfiltration over email
| Id | de039242-47e0-43fa-84d7-b6be24305349 | 
| Rulename | Dataverse - Terminated employee exfiltration over email | 
| Description | This query identifies Dataverse exfiltration via email by terminated employees. | 
| Severity | High | 
| Tactics | Exfiltration | 
| Techniques | T1639 T1567  | 
| Required data connectors | AzureActiveDirectoryIdentityProtection IdentityInfo MicrosoftThreatProtection  | 
| Kind | Scheduled | 
| Query frequency | 1h | 
| Query period | 14d | 
| Trigger threshold | 0 | 
| Trigger operator | gt | 
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Terminated employee exfiltration over email.yaml | 
| Version | 3.2.0 | 
| Arm template | de039242-47e0-43fa-84d7-b6be24305349.json | 
// Note this detection relies upon the user's UPN matching their email address.
// UEBA can provide more accurate data if enabled.
let query_frequency = 1h;
let allowed_destination_smtp_domains = dynamic([
// Specify a list of recipient domains to exclude from alerting.
// Example:
// "microsoft.com", "contoso.com"
    ]);
let exfiltration_alert_users = SecurityAlert
    | where Tactics has 'Exfiltration' and Entities has_all ('account', '32780')
    | mv-expand DataverseEntities = todynamic(Entities)
    | where DataverseEntities.AppId == 32780
    | extend InstanceUrl = tostring(DataverseEntities.InstanceName)
    | mv-expand AccountEntities = todynamic(Entities)
    | where AccountEntities.Type == 'account'
    | extend
        AccountName = tostring(AccountEntities.Name),
        UPNSuffix = tostring(AccountEntities.UPNSuffix)
    | summarize InstanceUrls = make_set(InstanceUrl, 100) by AccountName, UPNSuffix
    | extend UserId = tolower(strcat(AccountName, "@", UPNSuffix));
exfiltration_alert_users
| join kind=inner (
    MSBizAppsTerminatedEmployees
    | project UserId = tolower(UserPrincipalName), NotificationDate
    | where startofday(NotificationDate) <= startofday(now()))
    // Uncomment the below KQL if UEBA is available to gain more accurate
    // email address data:
    // | join kind=leftouter (_ASIM_IdentityInfo) on $left.UserId == $right.Username
    // | extend UserId = iif(UserId == UserMailAddress or isempty(UserMailAddress), UserId, UserMailAddress))
    on UserId
| join kind=inner (
    EmailEvents
    | where TimeGenerated >= ago (query_frequency)
    | where EmailDirection == "Outbound" and AttachmentCount > 0
    | extend RecipientDomain = tolower(split(RecipientEmailAddress, '@')[1])
    | where RecipientDomain !in (allowed_destination_smtp_domains)
    | summarize
        RecipientAddresses = make_set(RecipientEmailAddress, 1000),
        Subject = make_set(Subject, 1000)
        by SenderAddress = tolower(SenderMailFromAddress), SenderIPv4)
    on $left.UserId == $right.SenderAddress
| mv-expand InstanceUrl = InstanceUrls to typeof(string)
| extend
    CloudAppId = int(32780),
    AccountName = tostring(split(UserId, "@")[0]),
    UPNSuffix = tostring(split(UserId, "@")[1])
| project
    UserId,
    InstanceUrl,
    SenderIPv4,
    RecipientAddresses,
    Subject,
    AccountName,
    UPNSuffix,
    CloudAppId
kind: Scheduled
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: Email attachment sent externally by terminated user following Dataverse exfiltration alerts
  alertDescriptionFormat: 'Departing or terminated user {{UserId}} was found to send email to external domains not on the allowed list: {{RecipientAddresses}}'
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: AccountName
    identifier: Name
  - columnName: UPNSuffix
    identifier: UPNSuffix
- entityType: IP
  fieldMappings:
  - columnName: SenderIPv4
    identifier: Address
- entityType: CloudApplication
  fieldMappings:
  - columnName: CloudAppId
    identifier: AppId
  - columnName: InstanceUrl
    identifier: InstanceName
description: This query identifies Dataverse exfiltration via email by terminated employees.
severity: High
queryFrequency: 1h
triggerThreshold: 0
relevantTechniques:
- T1639
- T1567
status: Available
tactics:
- Exfiltration
name: Dataverse - Terminated employee exfiltration over email
id: de039242-47e0-43fa-84d7-b6be24305349
query: |
  // Note this detection relies upon the user's UPN matching their email address.
  // UEBA can provide more accurate data if enabled.
  let query_frequency = 1h;
  let allowed_destination_smtp_domains = dynamic([
  // Specify a list of recipient domains to exclude from alerting.
  // Example:
  // "microsoft.com", "contoso.com"
      ]);
  let exfiltration_alert_users = SecurityAlert
      | where Tactics has 'Exfiltration' and Entities has_all ('account', '32780')
      | mv-expand DataverseEntities = todynamic(Entities)
      | where DataverseEntities.AppId == 32780
      | extend InstanceUrl = tostring(DataverseEntities.InstanceName)
      | mv-expand AccountEntities = todynamic(Entities)
      | where AccountEntities.Type == 'account'
      | extend
          AccountName = tostring(AccountEntities.Name),
          UPNSuffix = tostring(AccountEntities.UPNSuffix)
      | summarize InstanceUrls = make_set(InstanceUrl, 100) by AccountName, UPNSuffix
      | extend UserId = tolower(strcat(AccountName, "@", UPNSuffix));
  exfiltration_alert_users
  | join kind=inner (
      MSBizAppsTerminatedEmployees
      | project UserId = tolower(UserPrincipalName), NotificationDate
      | where startofday(NotificationDate) <= startofday(now()))
      // Uncomment the below KQL if UEBA is available to gain more accurate
      // email address data:
      // | join kind=leftouter (_ASIM_IdentityInfo) on $left.UserId == $right.Username
      // | extend UserId = iif(UserId == UserMailAddress or isempty(UserMailAddress), UserId, UserMailAddress))
      on UserId
  | join kind=inner (
      EmailEvents
      | where TimeGenerated >= ago (query_frequency)
      | where EmailDirection == "Outbound" and AttachmentCount > 0
      | extend RecipientDomain = tolower(split(RecipientEmailAddress, '@')[1])
      | where RecipientDomain !in (allowed_destination_smtp_domains)
      | summarize
          RecipientAddresses = make_set(RecipientEmailAddress, 1000),
          Subject = make_set(Subject, 1000)
          by SenderAddress = tolower(SenderMailFromAddress), SenderIPv4)
      on $left.UserId == $right.SenderAddress
  | mv-expand InstanceUrl = InstanceUrls to typeof(string)
  | extend
      CloudAppId = int(32780),
      AccountName = tostring(split(UserId, "@")[0]),
      UPNSuffix = tostring(split(UserId, "@")[1])
  | project
      UserId,
      InstanceUrl,
      SenderIPv4,
      RecipientAddresses,
      Subject,
      AccountName,
      UPNSuffix,
      CloudAppId  
requiredDataConnectors:
- dataTypes:
  - EmailEvents
  connectorId: MicrosoftThreatProtection
- dataTypes:
  - SecurityAlert
  connectorId: AzureActiveDirectoryIdentityProtection
- dataTypes:
  - IdentityInfo
  connectorId: IdentityInfo
version: 3.2.0
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Terminated employee exfiltration over email.yaml
queryPeriod: 14d
{
  "$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/de039242-47e0-43fa-84d7-b6be24305349')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/de039242-47e0-43fa-84d7-b6be24305349')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDescriptionFormat": "Departing or terminated user {{UserId}} was found to send email to external domains not on the allowed list: {{RecipientAddresses}}",
          "alertDisplayNameFormat": "Email attachment sent externally by terminated user following Dataverse exfiltration alerts"
        },
        "alertRuleTemplateName": "de039242-47e0-43fa-84d7-b6be24305349",
        "customDetails": null,
        "description": "This query identifies Dataverse exfiltration via email by terminated employees.",
        "displayName": "Dataverse - Terminated employee exfiltration over email",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "UPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "SenderIPv4",
                "identifier": "Address"
              }
            ]
          },
          {
            "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 - Terminated employee exfiltration over email.yaml",
        "query": "// Note this detection relies upon the user's UPN matching their email address.\n// UEBA can provide more accurate data if enabled.\nlet query_frequency = 1h;\nlet allowed_destination_smtp_domains = dynamic([\n// Specify a list of recipient domains to exclude from alerting.\n// Example:\n// \"microsoft.com\", \"contoso.com\"\n    ]);\nlet exfiltration_alert_users = SecurityAlert\n    | where Tactics has 'Exfiltration' and Entities has_all ('account', '32780')\n    | mv-expand DataverseEntities = todynamic(Entities)\n    | where DataverseEntities.AppId == 32780\n    | extend InstanceUrl = tostring(DataverseEntities.InstanceName)\n    | mv-expand AccountEntities = todynamic(Entities)\n    | where AccountEntities.Type == 'account'\n    | extend\n        AccountName = tostring(AccountEntities.Name),\n        UPNSuffix = tostring(AccountEntities.UPNSuffix)\n    | summarize InstanceUrls = make_set(InstanceUrl, 100) by AccountName, UPNSuffix\n    | extend UserId = tolower(strcat(AccountName, \"@\", UPNSuffix));\nexfiltration_alert_users\n| join kind=inner (\n    MSBizAppsTerminatedEmployees\n    | project UserId = tolower(UserPrincipalName), NotificationDate\n    | where startofday(NotificationDate) <= startofday(now()))\n    // Uncomment the below KQL if UEBA is available to gain more accurate\n    // email address data:\n    // | join kind=leftouter (_ASIM_IdentityInfo) on $left.UserId == $right.Username\n    // | extend UserId = iif(UserId == UserMailAddress or isempty(UserMailAddress), UserId, UserMailAddress))\n    on UserId\n| join kind=inner (\n    EmailEvents\n    | where TimeGenerated >= ago (query_frequency)\n    | where EmailDirection == \"Outbound\" and AttachmentCount > 0\n    | extend RecipientDomain = tolower(split(RecipientEmailAddress, '@')[1])\n    | where RecipientDomain !in (allowed_destination_smtp_domains)\n    | summarize\n        RecipientAddresses = make_set(RecipientEmailAddress, 1000),\n        Subject = make_set(Subject, 1000)\n        by SenderAddress = tolower(SenderMailFromAddress), SenderIPv4)\n    on $left.UserId == $right.SenderAddress\n| mv-expand InstanceUrl = InstanceUrls to typeof(string)\n| extend\n    CloudAppId = int(32780),\n    AccountName = tostring(split(UserId, \"@\")[0]),\n    UPNSuffix = tostring(split(UserId, \"@\")[1])\n| project\n    UserId,\n    InstanceUrl,\n    SenderIPv4,\n    RecipientAddresses,\n    Subject,\n    AccountName,\n    UPNSuffix,\n    CloudAppId\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P14D",
        "severity": "High",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Exfiltration"
        ],
        "techniques": [
          "T1567"
        ],
        "templateVersion": "3.2.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}