Detection rules › Kusto
Power Apps - Bulk sharing of Power Apps to newly created guest users
Identifies unusual bulk sharing, based on a predefined threshold in the query, of Power Apps to newly created Microsoft Entra guest users.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Resource Development | |
| Initial Access | |
| Lateral Movement |
Telemetry coverage
Rules detecting the same action
These rules filter on the same operation.
- [Entra ID] Authentication Method Changed for Privileged Account (Kusto)
- Account Created and Deleted in Short Timeframe (Kusto)
- Account Created And Deleted Within A Close Time Frame (Sigma)
- Account created from non-approved sources (Kusto)
- Account created or deleted by non-approved user (Kusto)
- Addition of a Temporary Access Pass to a Privileged Account (Kusto)
- Authentication Method Changed for Privileged Account (Kusto)
- Authentication Methods Changed for Privileged Account (Kusto)
Rule body
id: 943acfa0-9285-4eb0-a9c0-42e36177ef19
kind: Scheduled
name: Power Apps - Bulk sharing of Power Apps to newly created guest users
description: Identifies unusual bulk sharing, based on a predefined threshold in the
query, of Power Apps to newly created Microsoft Entra guest users.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: PowerPlatformAdmin
dataTypes:
- PowerPlatformAdminActivity
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- ResourceDevelopment
- InitialAccess
- LateralMovement
relevantTechniques:
- T1587
- T1566
- T1534
query: |
////////////
// threshold = If the number of unique accounts that a power app is shared with is greater than
// threshold than it'll trigger an alert. A threshold of 5 is good to start with.
// However, if this is giving too many false positives, please adjust the threshold.
////////////
let threshold = 5;
////////////
// Please replace the allowed_domains with a list of domains of your partners/sibling orgs
// with whom you generally share power apps with. This will allow us to filter
// legitimate bulk sharing attempts. Avoid using domains such as gmail, outlook, etc.
///////////
let allowed_domains = pack_array("contoso.com");
let query_frequency = 1h;
let query_lookback = 14d;
PowerPlatformAdminActivity
| where TimeGenerated >= ago(query_frequency)
| where EventOriginalType == "PowerAppPermissionEdited"
| extend Properties = tostring(PropertyCollection)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend TargetPrincipalId = extract(@'"targetuser.id","Value":"([^"]+)"', 1, Properties)
| join kind=leftouter (
AuditLogs
| where ActivityDateTime >= ago(query_lookback)
| where SourceSystem =~ "Azure AD" and OperationName == "Invite external user"
| where Result =~ "success"
| extend InvitedOrgEmail = tostring(parse_json(AdditionalDetails[5])['value'])
| extend InvitedOrgDomain = tostring(split(InvitedOrgEmail, "@")[1])
| where not(InvitedOrgDomain has_any(allowed_domains))
| extend
InvitedById = tostring(parse_json(InitiatedBy)['user']['id']),
InvitedByUPN = tostring(parse_json(InitiatedBy)['user']['userPrincipalName']),
InvitedEmail = tostring(parse_json(TargetResources[0])['userPrincipalName']),
InvitedId = tostring(parse_json(TargetResources[0])['id'])
| summarize by InvitedById, InvitedByUPN, InvitedEmail, InvitedId, InvitedOrgDomain)
on $left.TargetPrincipalId == $right.InvitedId
| where isnotempty(InvitedId)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
TargetedUsersCount=dcount(TargetPrincipalId),
TargetedObjectIds = make_set(TargetPrincipalId, 1000),
InvitedDomains = make_set(InvitedOrgDomain, 1000),
InvitedEmailAddresses = make_set(InvitedEmail, 1000)
by AppId, InvitedById, InvitedByUPN
| extend
PowerAppsEntityId = 27593,
AccountName = tostring(split(InvitedByUPN, '@')[0]),
UPNSuffix = tostring(split(InvitedByUPN, '@')[1])
| project
StartTime,
EndTime,
InvitedByUPN,
InvitedById,
InvitedDomains,
InvitedEmailAddresses,
TargetedUsersCount,
TargetedObjectIds,
AppId,
PowerAppsEntityId,
AccountName,
UPNSuffix
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: PowerAppsEntityId
- identifier: InstanceName
columnName: AppId
alertDetailsOverride:
alertDisplayNameFormat: Power Apps - app shared with recently created external guest
accounts
alertDescriptionFormat: '{{InvitedByUPN}} shared an app with {{TargetedUsersCount}}
recently added guest user accounts that are not on the list of allowed partner
domains. List of domain s {{InvitedDomains}}'
customDetails:
PowerAppsApp: AppId
version: 3.2.0
Stages and Predicates
Parameters
let threshold = 5;
let allowed_domains = pack_array("contoso.com");
let query_frequency = 1h;
let query_lookback = 14d;
Stage 1: source
PowerPlatformAdminActivity
Stage 2: where
| where TimeGenerated >= ago(query_frequency)
Stage 3: where
| where EventOriginalType == "PowerAppPermissionEdited"
Stage 4: extend (4 consecutive steps)
| extend Properties = tostring(PropertyCollection)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend TargetPrincipalId = extract(@'"targetuser.id","Value":"([^"]+)"', 1, Properties)
Stage 5: join
| join kind=leftouter (
AuditLogs
| where ActivityDateTime >= ago(query_lookback)
| where SourceSystem =~ "Azure AD" and OperationName == "Invite external user"
| where Result =~ "success"
| extend InvitedOrgEmail = tostring(parse_json(AdditionalDetails[5])['value'])
| extend InvitedOrgDomain = tostring(split(InvitedOrgEmail, "@")[1])
| where not(InvitedOrgDomain has_any(allowed_domains))
| extend
InvitedById = tostring(parse_json(InitiatedBy)['user']['id']),
InvitedByUPN = tostring(parse_json(InitiatedBy)['user']['userPrincipalName']),
InvitedEmail = tostring(parse_json(TargetResources[0])['userPrincipalName']),
InvitedId = tostring(parse_json(TargetResources[0])['id'])
| summarize by InvitedById, InvitedByUPN, InvitedEmail, InvitedId, InvitedOrgDomain)
on $left.TargetPrincipalId == $right.InvitedId
Stage 6: where
| where isnotempty(InvitedId)
Stage 7: summarize
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
TargetedUsersCount=dcount(TargetPrincipalId),
TargetedObjectIds = make_set(TargetPrincipalId, 1000),
InvitedDomains = make_set(InvitedOrgDomain, 1000),
InvitedEmailAddresses = make_set(InvitedEmail, 1000)
by AppId, InvitedById, InvitedByUPN
Stage 8: extend
| extend
PowerAppsEntityId = 27593,
AccountName = tostring(split(InvitedByUPN, '@')[0]),
UPNSuffix = tostring(split(InvitedByUPN, '@')[1])
Stage 9: project
| project
StartTime,
EndTime,
InvitedByUPN,
InvitedById,
InvitedDomains,
InvitedEmailAddresses,
TargetedUsersCount,
TargetedObjectIds,
AppId,
PowerAppsEntityId,
AccountName,
UPNSuffix
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
InvitedOrgDomain | match | allowed_domains | excludes:InvitedOrgDomain field:"InvitedOrgDomain" value:"allowed_domains" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventOriginalType | eq |
| field:"EventOriginalType" kind:eq value:"PowerAppPermissionEdited" |
InvitedId | is_not_null | field:"InvitedId" kind:is_not_null | |
OperationName | eq |
| field:"OperationName" kind:eq value:"Invite external user" |
Result | eq |
| field:"Result" kind:eq value:"success" |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"Azure AD" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | project |
AppId | project |
EndTime | project |
InvitedById | project |
InvitedByUPN | project |
InvitedDomains | project |
InvitedEmailAddresses | project |
PowerAppsEntityId | project |
StartTime | project |
TargetedObjectIds | project |
TargetedUsersCount | project |
UPNSuffix | project |