Detection rules › Kusto
Dataverse - New non-interactive identity granted access
Identifies API level access grants, either via the delegated permissions of a Microsoft Entra application or direct assignment within Dataverse as an application user.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation | |
| Persistence | |
| Lateral Movement |
Telemetry coverage
| Provider | Record / event type |
|---|---|
| Entra-AuditLogs | Update application |
| M365-CRM | any: Dataverse and model-driven apps audit (catch-all) |
Rules detecting the same action
These rules filter on the same operation.
- [Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions (Kusto)
- [Entra ID] Application Granted Administrative Permission to Assign Microsoft Entra ID Roles (Kusto)
- Added Credentials to Existing Application (Sigma)
- Added Owner To Application (Sigma)
- Admin promotion after Role Management Application Permission Grant (Kusto)
- App Granted Microsoft Permissions (Sigma)
- App Granted Privileged Delegated Or App Permissions (Sigma)
- Application AppID Uri Configuration Changes (Sigma)
Rule body
id: 682e230c-e5da-4085-8666-701d1f1be7de
kind: Scheduled
name: Dataverse - New non-interactive identity granted access
description: Identifies API level access grants, either via the delegated permissions
of a Microsoft Entra application or direct assignment within Dataverse as an application
user.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Dataverse
dataTypes:
- DataverseActivity
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- LateralMovement
- PrivilegeEscalation
relevantTechniques:
- T1098
- T0859
- T1078
query: |
let dataverse_app_id = "00000007-0000-0000-c000-000000000000";
let query_frequency = 1h;
let azure_ad_changes = AuditLogs
| where TimeGenerated >= ago(query_frequency)
| where OperationName =~ 'Update application'
| where TargetResources has dataverse_app_id
| extend TargetAppName = tostring(TargetResources[0].displayName)
| extend TargetAppId = tostring(TargetResources[0].id)
| extend UserId = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend ClientIp = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend NewData = tostring(parse_json(tostring(parse_json(TargetResources)[0].modifiedProperties))[0].newValue)
| where NewData has dataverse_app_id;
let dataverse_changes = DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where (Message == "Create" and EntityName == "systemuser" and parse_json(Fields)[0].Name == "applicationid")
| extend TargetAppId = tostring(Fields[0].Value);
union azure_ad_changes, dataverse_changes
| extend
CloudAppId = int(32780),
AccountName = tostring(split(UserId, '@')[0]),
UPNSuffix = tostring(split(UserId, '@')[1])
| project
TimeGenerated,
UserId,
ClientIp,
TargetAppName,
TargetAppId,
InstanceUrl,
CloudAppId,
AccountName,
UPNSuffix
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: CloudAppId
- identifier: InstanceName
columnName: InstanceUrl
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIp
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: TargetAppId
alertDetailsOverride:
alertDisplayNameFormat: Dataverse - new non-interactive access granted
alertDescriptionFormat: '{{UserId}} granted access to an Azure AD app {{{TargetAppName}}.
Check to validate this access was authorized.'
version: 3.2.0
Stages and Predicates
Parameters
let dataverse_app_id = "00000007-0000-0000-c000-000000000000";
let query_frequency = 1h;
union (2 sources)
Each leg below queries one source; the rule matches if any leg does. Sources: azure_ad_changes, dataverse_changes
Leg 1: azure_ad_changes
Leg 2: dataverse_changes
Applied to the combined result
| extend
CloudAppId = int(32780),
AccountName = tostring(split(UserId, '@')[0]),
UPNSuffix = tostring(split(UserId, '@')[1]) | project
TimeGenerated,
UserId,
ClientIp,
TargetAppName,
TargetAppId,
InstanceUrl,
CloudAppId,
AccountName,
UPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EntityName | eq |
| field:"EntityName" kind:eq value:"systemuser" |
Message | eq |
| field:"Message" kind:eq value:"Create" |
Name | eq |
| field:"Name" kind:eq value:"applicationid" |
NewData | match |
| field:"NewData" kind:match value:"00000007-0000-0000-c000-000000000000" |
OperationName | eq |
| field:"OperationName" kind:eq value:"Update application" |
TargetResources | match |
| field:"azure_ad::target_resources" kind:match value:"00000007-0000-0000-c000-000000000000" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | project |
ClientIp | project |
CloudAppId | project |
InstanceUrl | project |
TargetAppId | project |
TargetAppName | project |
TimeGenerated | project |
UPNSuffix | project |
UserId | project |