Detection rules › YARA-L
Entra ID Excessive Permission Changes to Application
Adding permissions to an application is expected, however if excessive permissions are changed at one time over a defined threshold, it may be worth validating.
References
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Azure | Update application |
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
// Copyright 2025 Google LLC. Licensed under Apache-2.0.
rule entra_id_excessive_permission_change_to_app {
meta:
author = "Google Cloud Security"
description = "Adding permissions to an application is expected, however if excessive permissions are changed at one time over a defined threshold, it may be worth validating."
rule_id = "mr_7e13e4fc-6a6d-45c6-af92-69f98b4c9805"
rule_name = "Entra ID Excessive Permission Changes to Application"
reference = "https://learn.microsoft.com/en-us/graph/permissions-reference"
type = "alert"
platform = "azure"
data_source = "azure ad"
severity = "Medium"
priority = "Medium"
events:
$app.metadata.event_type = "SERVICE_MODIFICATION"
$app.metadata.product_event_type = "Update application"
$app.metadata.product_name = "Azure AD Directory Audit"
$app.metadata.vendor_name = "Microsoft"
$app.security_result.action = "ALLOW"
(
(
//used to tune application updates that are not permission related or where permissions before and after are the same
$app.target.resource.attribute.labels["RequiredResourceAccess"] != "" and
$app.src.resource.attribute.labels["RequiredResourceAccess"] != ""
) or
$app.src.resource.attribute.labels["RequiredResourceAccess"] != $app.target.resource.attribute.labels["RequiredResourceAccess"]
)
$app.principal.user.userid = $userid
match:
$userid over 5m
outcome:
$risk_score = 65
$event_count = count_distinct($app.metadata.id)
$previous_permissions = array_distinct($app.src.resource.attribute.labels["RequiredResourceAccess"])
$previous_permissions_count = max(strings.count_substrings($app.src.resource.attribute.labels["RequiredResourceAccess"], "EntitlementId:"))
$current_permissions = array_distinct($app.target.resource.attribute.labels["RequiredResourceAccess"])
$current_permissions_count = max(strings.count_substrings($app.target.resource.attribute.labels["RequiredResourceAccess"], "EntitlementId:"))
$permission_delta = math.abs($current_permissions_count - $previous_permissions_count)
$principal_county_region = array_distinct($app.principal.ip_geo_artifact.location.country_or_region)
$principal_ip = array_distinct($app.principal.ip)
$target_application = array_distinct($app.target.resource.name)
condition:
//adjust the permission delta to your desired threshold
$app and $permission_delta > 5
}
YARA-L rule structure
Condition
Fires when at least one $app event in the 5m window and $permission_delta > 5.
Events
$app
metadata.product_event_type = "Update application"security_result.action = "ALLOW"target.resource.attribute.labels["RequiredResourceAccess"] is not nullsrc.resource.attribute.labels["RequiredResourceAccess"] is not null
Match and correlation
Outcome
Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.
| Field | Expression |
|---|---|
risk_score | 65 |
event_count | count_distinct($app.metadata.id) |
previous_permissions | array_distinct($app.src.resource.attribute.labels["RequiredResourceAccess"]) |
previous_permissions_count | max(strings.count_substrings($app.src.resource.attribute.labels["RequiredResourceAccess"], "EntitlementId:")) |
current_permissions | array_distinct($app.target.resource.attribute.labels["RequiredResourceAccess"]) |
current_permissions_count | max(strings.count_substrings($app.target.resource.attribute.labels["RequiredResourceAccess"], "EntitlementId:")) |
permission_delta | math.abs($current_permissions_count - $previous_permissions_count) |
principal_county_region | array_distinct($app.principal.ip_geo_artifact.location.country_or_region) |
principal_ip | array_distinct($app.principal.ip) |
target_application | array_distinct($app.target.resource.name) |