Dataverse - Guest user exfiltration following Power Platform defense impairment
Id | 39efbf4b-b347-4cc7-895e-99a868bf29ea |
Rulename | Dataverse - Guest user exfiltration following Power Platform defense impairment |
Description | Identifies a chain of events starting with disablement of Power Platform tenant isolation and removal of an environment’s access security group. These events are correlated with Dataverse exfiltration alerts associated with the impacted environment and recently created Microsoft Entra guest users. Note: Activate other Dataverse analytics rules with the MITRE tactic ‘Exfiltration’ before enabling this rule. |
Severity | High |
Tactics | DefenseEvasion Exfiltration |
Techniques | T1629 T1567 |
Required data connectors | AzureActiveDirectory AzureActiveDirectoryIdentityProtection PowerPlatformAdmin |
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 - Guest user exfiltration following Power Platform defense impairment.yaml |
Version | 3.2.0 |
Arm template | 39efbf4b-b347-4cc7-895e-99a868bf29ea.json |
let query_lookback = 14d;
let query_frequncy = 1h;
let defense_evasion_events = PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_lookback)
| where EventOriginalType == "TenantIsolationOperation"
| mv-expand PropertyCollection
| where PropertyCollection.Name == "powerplatform.analytics.resource.tenant.isolation_policy.enabled"
| where PropertyCollection.Value == "False"
| summarize
TenantIsolationRemovalTimestamp = max(TimeGenerated)
by SecurityDisablingUser = ActorName
| join kind=inner (
PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_lookback)
| where EventOriginalType == "EnvironmentPropertyChange"
| where PropertyCollection has "Property: SecurityGroupId, Old Value: , New Value: "
| mv-expand PropertyCollection
| extend
GroupRemovalTimestamp = TimeGenerated,
InstanceUrl = tostring(iif(PropertyCollection.Name == "powerplatform.analytics.resource.environment.url", PropertyCollection.Value, "")),
EnvironmentId = tostring(iif(PropertyCollection.Name == "powerplatform.analytics.resource.environment.name", PropertyCollection.Value, ""))
| summarize InstanceUrl = max(InstanceUrl), EnvironmentId = max(EnvironmentId) by GroupRemovalTimestamp, SecurityDisablingUser = ActorName)
on SecurityDisablingUser
| summarize
GroupRemovalTimestamp = max(GroupRemovalTimestamp),
TenantIsolationRemovalTimestamp = max(TenantIsolationRemovalTimestamp)
by SecurityDisablingUser, InstanceUrl, EnvironmentId;
let exfiltration_alerts = SecurityAlert
| where TimeGenerated >= ago(query_frequncy)
| where Tactics has "Exfiltration"
| where Entities has ('"AppId":32780')
| mv-expand todynamic(Entities)
| extend AlertUPN = iif(Entities.Type == "account", strcat(Entities.Name, "@", Entities.UPNSuffix), "")
| extend InstanceUrl = tostring(iif(Entities.AppId == 32780, Entities.InstanceName, ""))
| join kind=inner defense_evasion_events on InstanceUrl
| where StartTime > TenantIsolationRemovalTimestamp and StartTime > GroupRemovalTimestamp
| summarize InstanceUrl = max(InstanceUrl), AlertUPN = max(AlertUPN) by AlertName, SystemAlertId
| extend AlertDetails = bag_pack("AlertName", AlertName, "SystemAlertId", SystemAlertId)
| summarize AlertDetails = make_set(AlertDetails, 100) by AlertUPN, InstanceUrl
| join kind=inner (
AuditLogs
| where OperationName == "Update user"
| where Identity == "Microsoft Invitation Acceptance Portal"
| mv-expand TargetResources
| extend ModifiedProperties = TargetResources.modifiedProperties
| mv-expand ModifiedProperties
| where ModifiedProperties.displayName == "AcceptedAs"
| summarize RedeemTime = max(TimeGenerated) by GuestUser = tostring(parse_json(replace_regex(tostring(ModifiedProperties.newValue), "\\r", ""))[0]))
on $left.AlertUPN == $right.GuestUser;
defense_evasion_events
| join kind=inner exfiltration_alerts on InstanceUrl
| extend
AccountName = tostring(split(SecurityDisablingUser, "@")[0]),
UPNSuffix = tostring(split(SecurityDisablingUser, "@")[1]),
GuestAccountName = tostring(split(GuestUser, "@")[0]),
GuestUPNSuffix = tostring(split(GuestUser, "@")[0]),
DataverseId = 32780
| project
SecurityDisablingUser,
GuestUser,
AlertDetails,
TenantIsolationRemovalTimestamp,
GroupRemovalTimestamp,
InstanceUrl,
EnvironmentId,
AccountName,
UPNSuffix,
GuestAccountName,
GuestUPNSuffix,
DataverseId
requiredDataConnectors:
- connectorId: PowerPlatformAdmin
dataTypes:
- PowerPlatformAdminActivity
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- connectorId: AzureActiveDirectoryIdentityProtection
dataTypes:
- SecurityAlert
alertDetailsOverride:
alertDisplayNameFormat: 'Dataverse - exfiltration alerts following defense impairment in {{InstanceUrl}} '
alertDescriptionFormat: '{{SecurityDisablingUser}} disabled Power Platform tenant isolation and removed the security group used to control access to {{{InstanceUrl}}. Exfiltration alerts associated with guest users were then detected from user {{{GuestUser}}'
query: |
let query_lookback = 14d;
let query_frequncy = 1h;
let defense_evasion_events = PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_lookback)
| where EventOriginalType == "TenantIsolationOperation"
| mv-expand PropertyCollection
| where PropertyCollection.Name == "powerplatform.analytics.resource.tenant.isolation_policy.enabled"
| where PropertyCollection.Value == "False"
| summarize
TenantIsolationRemovalTimestamp = max(TimeGenerated)
by SecurityDisablingUser = ActorName
| join kind=inner (
PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_lookback)
| where EventOriginalType == "EnvironmentPropertyChange"
| where PropertyCollection has "Property: SecurityGroupId, Old Value: , New Value: "
| mv-expand PropertyCollection
| extend
GroupRemovalTimestamp = TimeGenerated,
InstanceUrl = tostring(iif(PropertyCollection.Name == "powerplatform.analytics.resource.environment.url", PropertyCollection.Value, "")),
EnvironmentId = tostring(iif(PropertyCollection.Name == "powerplatform.analytics.resource.environment.name", PropertyCollection.Value, ""))
| summarize InstanceUrl = max(InstanceUrl), EnvironmentId = max(EnvironmentId) by GroupRemovalTimestamp, SecurityDisablingUser = ActorName)
on SecurityDisablingUser
| summarize
GroupRemovalTimestamp = max(GroupRemovalTimestamp),
TenantIsolationRemovalTimestamp = max(TenantIsolationRemovalTimestamp)
by SecurityDisablingUser, InstanceUrl, EnvironmentId;
let exfiltration_alerts = SecurityAlert
| where TimeGenerated >= ago(query_frequncy)
| where Tactics has "Exfiltration"
| where Entities has ('"AppId":32780')
| mv-expand todynamic(Entities)
| extend AlertUPN = iif(Entities.Type == "account", strcat(Entities.Name, "@", Entities.UPNSuffix), "")
| extend InstanceUrl = tostring(iif(Entities.AppId == 32780, Entities.InstanceName, ""))
| join kind=inner defense_evasion_events on InstanceUrl
| where StartTime > TenantIsolationRemovalTimestamp and StartTime > GroupRemovalTimestamp
| summarize InstanceUrl = max(InstanceUrl), AlertUPN = max(AlertUPN) by AlertName, SystemAlertId
| extend AlertDetails = bag_pack("AlertName", AlertName, "SystemAlertId", SystemAlertId)
| summarize AlertDetails = make_set(AlertDetails, 100) by AlertUPN, InstanceUrl
| join kind=inner (
AuditLogs
| where OperationName == "Update user"
| where Identity == "Microsoft Invitation Acceptance Portal"
| mv-expand TargetResources
| extend ModifiedProperties = TargetResources.modifiedProperties
| mv-expand ModifiedProperties
| where ModifiedProperties.displayName == "AcceptedAs"
| summarize RedeemTime = max(TimeGenerated) by GuestUser = tostring(parse_json(replace_regex(tostring(ModifiedProperties.newValue), "\\r", ""))[0]))
on $left.AlertUPN == $right.GuestUser;
defense_evasion_events
| join kind=inner exfiltration_alerts on InstanceUrl
| extend
AccountName = tostring(split(SecurityDisablingUser, "@")[0]),
UPNSuffix = tostring(split(SecurityDisablingUser, "@")[1]),
GuestAccountName = tostring(split(GuestUser, "@")[0]),
GuestUPNSuffix = tostring(split(GuestUser, "@")[0]),
DataverseId = 32780
| project
SecurityDisablingUser,
GuestUser,
AlertDetails,
TenantIsolationRemovalTimestamp,
GroupRemovalTimestamp,
InstanceUrl,
EnvironmentId,
AccountName,
UPNSuffix,
GuestAccountName,
GuestUPNSuffix,
DataverseId
kind: Scheduled
severity: High
eventGroupingSettings:
aggregationKind: SingleAlert
triggerThreshold: 0
customDetails:
Environment: EnvironmentId
queryFrequency: 1h
id: 39efbf4b-b347-4cc7-895e-99a868bf29ea
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Guest user exfiltration following Power Platform defense impairment.yaml
tactics:
- DefenseEvasion
- Exfiltration
triggerOperator: gt
description: |
Identifies a chain of events starting with disablement of Power Platform tenant isolation and removal of an environment's access security group. These events are correlated with Dataverse exfiltration alerts associated with the impacted environment and recently created Microsoft Entra guest users.
Note: Activate other Dataverse analytics rules with the MITRE tactic 'Exfiltration' before enabling this rule.
status: Available
queryPeriod: 14d
name: Dataverse - Guest user exfiltration following Power Platform defense impairment
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: Account
fieldMappings:
- identifier: Name
columnName: GuestAccountName
- identifier: UPNSuffix
columnName: GuestUPNSuffix
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: DataverseId
- identifier: InstanceName
columnName: InstanceUrl
relevantTechniques:
- T1629
- T1567
version: 3.2.0
{
"$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/39efbf4b-b347-4cc7-895e-99a868bf29ea')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/39efbf4b-b347-4cc7-895e-99a868bf29ea')]",
"properties": {
"alertDetailsOverride": {
"alertDescriptionFormat": "{{SecurityDisablingUser}} disabled Power Platform tenant isolation and removed the security group used to control access to {{{InstanceUrl}}. Exfiltration alerts associated with guest users were then detected from user {{{GuestUser}}",
"alertDisplayNameFormat": "Dataverse - exfiltration alerts following defense impairment in {{InstanceUrl}} "
},
"alertRuleTemplateName": "39efbf4b-b347-4cc7-895e-99a868bf29ea",
"customDetails": {
"Environment": "EnvironmentId"
},
"description": "Identifies a chain of events starting with disablement of Power Platform tenant isolation and removal of an environment's access security group. These events are correlated with Dataverse exfiltration alerts associated with the impacted environment and recently created Microsoft Entra guest users.\n\nNote: Activate other Dataverse analytics rules with the MITRE tactic 'Exfiltration' before enabling this rule.\n",
"displayName": "Dataverse - Guest user exfiltration following Power Platform defense impairment",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "AccountName",
"identifier": "Name"
},
{
"columnName": "UPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "GuestAccountName",
"identifier": "Name"
},
{
"columnName": "GuestUPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "CloudApplication",
"fieldMappings": [
{
"columnName": "DataverseId",
"identifier": "AppId"
},
{
"columnName": "InstanceUrl",
"identifier": "InstanceName"
}
]
}
],
"eventGroupingSettings": {
"aggregationKind": "SingleAlert"
},
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Dataverse - Guest user exfiltration following Power Platform defense impairment.yaml",
"query": "let query_lookback = 14d;\nlet query_frequncy = 1h;\nlet defense_evasion_events = PowerPlatformAdminActivity\n | where TimeGenerated >= ago(query_lookback)\n | where EventOriginalType == \"TenantIsolationOperation\"\n | mv-expand PropertyCollection\n | where PropertyCollection.Name == \"powerplatform.analytics.resource.tenant.isolation_policy.enabled\"\n | where PropertyCollection.Value == \"False\"\n | summarize\n TenantIsolationRemovalTimestamp = max(TimeGenerated)\n by SecurityDisablingUser = ActorName\n | join kind=inner (\n PowerPlatformAdminActivity\n | where TimeGenerated >= ago(query_lookback)\n | where EventOriginalType == \"EnvironmentPropertyChange\"\n | where PropertyCollection has \"Property: SecurityGroupId, Old Value: , New Value: \"\n | mv-expand PropertyCollection\n | extend\n GroupRemovalTimestamp = TimeGenerated,\n InstanceUrl = tostring(iif(PropertyCollection.Name == \"powerplatform.analytics.resource.environment.url\", PropertyCollection.Value, \"\")),\n EnvironmentId = tostring(iif(PropertyCollection.Name == \"powerplatform.analytics.resource.environment.name\", PropertyCollection.Value, \"\"))\n | summarize InstanceUrl = max(InstanceUrl), EnvironmentId = max(EnvironmentId) by GroupRemovalTimestamp, SecurityDisablingUser = ActorName)\n on SecurityDisablingUser\n | summarize\n GroupRemovalTimestamp = max(GroupRemovalTimestamp),\n TenantIsolationRemovalTimestamp = max(TenantIsolationRemovalTimestamp)\n by SecurityDisablingUser, InstanceUrl, EnvironmentId;\nlet exfiltration_alerts = SecurityAlert\n | where TimeGenerated >= ago(query_frequncy)\n | where Tactics has \"Exfiltration\"\n | where Entities has ('\"AppId\":32780')\n | mv-expand todynamic(Entities)\n | extend AlertUPN = iif(Entities.Type == \"account\", strcat(Entities.Name, \"@\", Entities.UPNSuffix), \"\")\n | extend InstanceUrl = tostring(iif(Entities.AppId == 32780, Entities.InstanceName, \"\"))\n | join kind=inner defense_evasion_events on InstanceUrl\n | where StartTime > TenantIsolationRemovalTimestamp and StartTime > GroupRemovalTimestamp\n | summarize InstanceUrl = max(InstanceUrl), AlertUPN = max(AlertUPN) by AlertName, SystemAlertId\n | extend AlertDetails = bag_pack(\"AlertName\", AlertName, \"SystemAlertId\", SystemAlertId)\n | summarize AlertDetails = make_set(AlertDetails, 100) by AlertUPN, InstanceUrl\n | join kind=inner (\n AuditLogs\n | where OperationName == \"Update user\"\n | where Identity == \"Microsoft Invitation Acceptance Portal\"\n | mv-expand TargetResources\n | extend ModifiedProperties = TargetResources.modifiedProperties\n | mv-expand ModifiedProperties\n | where ModifiedProperties.displayName == \"AcceptedAs\"\n | summarize RedeemTime = max(TimeGenerated) by GuestUser = tostring(parse_json(replace_regex(tostring(ModifiedProperties.newValue), \"\\\\r\", \"\"))[0]))\n on $left.AlertUPN == $right.GuestUser;\ndefense_evasion_events\n| join kind=inner exfiltration_alerts on InstanceUrl\n| extend\n AccountName = tostring(split(SecurityDisablingUser, \"@\")[0]),\n UPNSuffix = tostring(split(SecurityDisablingUser, \"@\")[1]),\n GuestAccountName = tostring(split(GuestUser, \"@\")[0]),\n GuestUPNSuffix = tostring(split(GuestUser, \"@\")[0]),\n DataverseId = 32780\n| project\n SecurityDisablingUser,\n GuestUser,\n AlertDetails,\n TenantIsolationRemovalTimestamp,\n GroupRemovalTimestamp,\n InstanceUrl,\n EnvironmentId,\n AccountName,\n UPNSuffix,\n GuestAccountName,\n GuestUPNSuffix,\n DataverseId\n",
"queryFrequency": "PT1H",
"queryPeriod": "P14D",
"severity": "High",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"DefenseEvasion",
"Exfiltration"
],
"techniques": [
"T1567"
],
"templateVersion": "3.2.0",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}