Power Apps - Multiple users access a malicious link after launching new app
| Id | 4bd7e93a-0646-4e02-8dcb-aa16d16618f4 |
| Rulename | Power Apps - Multiple users access a malicious link after launching new app |
| Description | Identifies a chain of events, where a new Power App is created, followed by mulitple users launching the app within the detection window and clicking on the same malicious URL. |
| Severity | High |
| Tactics | InitialAccess |
| Techniques | T1189 T1566 |
| Required data connectors | AzureActiveDirectoryIdentityProtection MicrosoftDefenderThreatIntelligence MicrosoftThreatProtection PowerPlatformAdmin ThreatIntelligence ThreatIntelligenceTaxii |
| 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/Power Apps - Multiple users access a malicious link after launching new app.yaml |
| Version | 3.2.0 |
| Arm template | 4bd7e93a-0646-4e02-8dcb-aa16d16618f4.json |
// Define a threshold (distinct_user_launch_threshold) for
// the minimum number of users who launched an app
// to be in scope of this detection
let distinct_user_launch_threshold = 2;
// Define a threshold for the minumum number of users
// who clicked the same malicious link after launching the app
// to be in scope of this detection
let distinct_user_url_click_threshold = 2;
let query_frequency = 1h;
let query_lookback = 14d;
let new_app_creation_activity = materialize(
PowerPlatformAdminActivity
| where TimeGenerated >= ago (query_lookback)
| where EventOriginalType == "CreatePowerApp"
| extend Properties = tostring(PropertyCollection)
| extend SrcIpAddr = extract(@'"enduser.ip_address","Value":"([^"]+)"', 1, Properties)
| extend SrcIpAddr = iif(SrcIpAddr startswith '::ffff:', replace_string(SrcIpAddr, '::ffff:', ''), SrcIpAddr)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend
AppName = extract(@'"powerplatform.analytics.resource.power_app.display_name","Value":"([^"]+)"', 1, Properties),
EnvironmentId = extract(@'"powerplatform.analytics.resource.environment.id","Value":"([^"]+)"', 1, Properties)
| project-rename
AppCreatedTime = TimeGenerated,
AppCreator = ActorName,
AppCreatorIpAddr = SrcIpAddr
);
let distinct_apps = new_app_creation_activity
| distinct AppName;
let new_app_launch_activity = materialize(
new_app_creation_activity
| join kind=inner (
PowerPlatformAdminActivity
| where TimeGenerated >= ago (query_lookback)
| where EventOriginalType == "LaunchPowerApp"
| where PropertyCollection has_any (distinct_apps)
| extend Properties = tostring(PropertyCollection)
| extend AppName = extract(@'"powerplatform.analytics.resource.power_app.display_name","Value":"([^"]+)"', 1, Properties)
| summarize FirstAppLaunchTime = min(TimeGenerated) by ActorName, AppName)
on AppName
| where FirstAppLaunchTime > AppCreatedTime
);
let new_app_launch_users = new_app_launch_activity
| summarize LaunchCount = dcount(ActorName) by AppName
| where LaunchCount > distinct_user_launch_threshold
| join kind=inner new_app_launch_activity on AppName
| summarize
by
ActorName,
FirstAppLaunchTime,
AppName,
AppId,
EnvironmentId,
AppCreator,
AppCreatorIpAddr;
let detected_urls = union isfuzzy=true
(
SecurityAlert
| where TimeGenerated >= ago (query_lookback)
| where Entities has_cs '"Type":"url"'
| mv-expand todynamic(Entities)
| where tostring(Entities.Type) == "url"
| project Url = tostring(Entities.Url), Source = "SecurityAlert"
),
(
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(query_lookback)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where isnotempty(isnotempty(Url))
| project Url, Source = "ThreatIntelligence"
)
| summarize by Url, Source;
let url_click_events = materialize(
union isfuzzy=true
(
UrlClickEvents
| where TimeGenerated >= ago(query_frequency)
| where isnotempty(ThreatTypes)
| join kind=inner (new_app_launch_users) on $left.AccountUpn == $right.ActorName
| where TimeGenerated between (FirstAppLaunchTime .. (FirstAppLaunchTime + 1h))
| summarize by ActorName, Url, Source = "MicrosoftDefender"
),
(
_Im_WebSession
| where TimeGenerated >= ago(query_frequency)
| join kind=inner (new_app_launch_users) on $left.SrcUsername == $right.ActorName
| join kind=inner (detected_urls) on Url
| where TimeGenerated between (FirstAppLaunchTime .. (FirstAppLaunchTime + 1h))
| summarize by ActorName, Url, Source
)
);
let distinct_url_click_events_count = toscalar(
url_click_events
| summarize DistinctUserCount = dcount(ActorName) by Url
| where DistinctUserCount > distinct_user_url_click_threshold
| summarize sum(DistinctUserCount)
);
url_click_events
| summarize DistinctUserCount = dcount(ActorName) by Url
| where DistinctUserCount >= distinct_user_url_click_threshold
| join kind=inner url_click_events on Url
| join kind=inner (new_app_launch_users) on ActorName
| extend
PowerAppsEntityId = 27593,
DataverseId = 32780,
AccountName = tostring(split(ActorName, '@')[0]),
UPNSuffix = tostring(split(ActorName, '@')[1])
| project
FirstAppLaunchTime,
AppCreator,
AppName,
AppId,
ImpactedUser = ActorName,
AccountName,
UPNSuffix,
EnvironmentId,
Url,
Source,
PowerAppsEntityId
description: Identifies a chain of events, where a new Power App is created, followed by mulitple users launching the app within the detection window and clicking on the same malicious URL.
version: 3.2.0
triggerThreshold: 0
tactics:
- InitialAccess
queryPeriod: 14d
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Business Applications/Analytic Rules/Power Apps - Multiple users access a malicious link after launching new app.yaml
triggerOperator: gt
status: Available
alertDetailsOverride:
alertDisplayNameFormat: 'Possible malicious app detected - {{AppName}} '
alertDescriptionFormat: 'Multiple users opened a malicious link after launching app {{AppName}}. Click here to navigate to the Power Apps Portal to examine the app: https://make.powerapps.com/environments/{{EnvironmentId}}/apps'
eventGroupingSettings:
aggregationKind: AlertPerResult
id: 4bd7e93a-0646-4e02-8dcb-aa16d16618f4
name: Power Apps - Multiple users access a malicious link after launching new app
queryFrequency: 1h
severity: High
customDetails:
PowerAppsApp: AppId
PowerAppsAppName: AppName
AppCreator: AppCreator
Environment: EnvironmentId
kind: Scheduled
entityMappings:
- fieldMappings:
- columnName: PowerAppsEntityId
identifier: AppId
- columnName: AppName
identifier: InstanceName
entityType: CloudApplication
- fieldMappings:
- columnName: Url
identifier: Url
entityType: URL
- fieldMappings:
- columnName: AppCreator
identifier: FullName
entityType: Account
- fieldMappings:
- columnName: UPNSuffix
identifier: UPNSuffix
- columnName: AccountName
identifier: Name
entityType: Account
relevantTechniques:
- T1189
- T1566
query: |
// Define a threshold (distinct_user_launch_threshold) for
// the minimum number of users who launched an app
// to be in scope of this detection
let distinct_user_launch_threshold = 2;
// Define a threshold for the minumum number of users
// who clicked the same malicious link after launching the app
// to be in scope of this detection
let distinct_user_url_click_threshold = 2;
let query_frequency = 1h;
let query_lookback = 14d;
let new_app_creation_activity = materialize(
PowerPlatformAdminActivity
| where TimeGenerated >= ago (query_lookback)
| where EventOriginalType == "CreatePowerApp"
| extend Properties = tostring(PropertyCollection)
| extend SrcIpAddr = extract(@'"enduser.ip_address","Value":"([^"]+)"', 1, Properties)
| extend SrcIpAddr = iif(SrcIpAddr startswith '::ffff:', replace_string(SrcIpAddr, '::ffff:', ''), SrcIpAddr)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend
AppName = extract(@'"powerplatform.analytics.resource.power_app.display_name","Value":"([^"]+)"', 1, Properties),
EnvironmentId = extract(@'"powerplatform.analytics.resource.environment.id","Value":"([^"]+)"', 1, Properties)
| project-rename
AppCreatedTime = TimeGenerated,
AppCreator = ActorName,
AppCreatorIpAddr = SrcIpAddr
);
let distinct_apps = new_app_creation_activity
| distinct AppName;
let new_app_launch_activity = materialize(
new_app_creation_activity
| join kind=inner (
PowerPlatformAdminActivity
| where TimeGenerated >= ago (query_lookback)
| where EventOriginalType == "LaunchPowerApp"
| where PropertyCollection has_any (distinct_apps)
| extend Properties = tostring(PropertyCollection)
| extend AppName = extract(@'"powerplatform.analytics.resource.power_app.display_name","Value":"([^"]+)"', 1, Properties)
| summarize FirstAppLaunchTime = min(TimeGenerated) by ActorName, AppName)
on AppName
| where FirstAppLaunchTime > AppCreatedTime
);
let new_app_launch_users = new_app_launch_activity
| summarize LaunchCount = dcount(ActorName) by AppName
| where LaunchCount > distinct_user_launch_threshold
| join kind=inner new_app_launch_activity on AppName
| summarize
by
ActorName,
FirstAppLaunchTime,
AppName,
AppId,
EnvironmentId,
AppCreator,
AppCreatorIpAddr;
let detected_urls = union isfuzzy=true
(
SecurityAlert
| where TimeGenerated >= ago (query_lookback)
| where Entities has_cs '"Type":"url"'
| mv-expand todynamic(Entities)
| where tostring(Entities.Type) == "url"
| project Url = tostring(Entities.Url), Source = "SecurityAlert"
),
(
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(query_lookback)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where isnotempty(isnotempty(Url))
| project Url, Source = "ThreatIntelligence"
)
| summarize by Url, Source;
let url_click_events = materialize(
union isfuzzy=true
(
UrlClickEvents
| where TimeGenerated >= ago(query_frequency)
| where isnotempty(ThreatTypes)
| join kind=inner (new_app_launch_users) on $left.AccountUpn == $right.ActorName
| where TimeGenerated between (FirstAppLaunchTime .. (FirstAppLaunchTime + 1h))
| summarize by ActorName, Url, Source = "MicrosoftDefender"
),
(
_Im_WebSession
| where TimeGenerated >= ago(query_frequency)
| join kind=inner (new_app_launch_users) on $left.SrcUsername == $right.ActorName
| join kind=inner (detected_urls) on Url
| where TimeGenerated between (FirstAppLaunchTime .. (FirstAppLaunchTime + 1h))
| summarize by ActorName, Url, Source
)
);
let distinct_url_click_events_count = toscalar(
url_click_events
| summarize DistinctUserCount = dcount(ActorName) by Url
| where DistinctUserCount > distinct_user_url_click_threshold
| summarize sum(DistinctUserCount)
);
url_click_events
| summarize DistinctUserCount = dcount(ActorName) by Url
| where DistinctUserCount >= distinct_user_url_click_threshold
| join kind=inner url_click_events on Url
| join kind=inner (new_app_launch_users) on ActorName
| extend
PowerAppsEntityId = 27593,
DataverseId = 32780,
AccountName = tostring(split(ActorName, '@')[0]),
UPNSuffix = tostring(split(ActorName, '@')[1])
| project
FirstAppLaunchTime,
AppCreator,
AppName,
AppId,
ImpactedUser = ActorName,
AccountName,
UPNSuffix,
EnvironmentId,
Url,
Source,
PowerAppsEntityId
requiredDataConnectors:
- dataTypes:
- PowerPlatformAdminActivity
connectorId: PowerPlatformAdmin
- dataTypes:
- UrlClickEvents
connectorId: MicrosoftThreatProtection
- dataTypes:
- ThreatIntelligenceIndicator
connectorId: ThreatIntelligence
- dataTypes:
- ThreatIntelligenceIndicator
connectorId: ThreatIntelligenceTaxii
- dataTypes:
- ThreatIntelligenceIndicator
connectorId: MicrosoftDefenderThreatIntelligence
- dataTypes:
- ThreatIntelligenceIndicator
connectorId: ThreatIntelligence
- dataTypes:
- ThreatIntelligenceIndicator
connectorId: ThreatIntelligenceTaxii
- dataTypes:
- ThreatIntelligenceIndicator
connectorId: MicrosoftDefenderThreatIntelligence
- dataTypes:
- UrlClickEvents
connectorId: MicrosoftThreatProtection
- dataTypes:
- SecurityAlert
connectorId: AzureActiveDirectoryIdentityProtection