Detection rules › Splunk

O365 Privileged Role Assigned To Service Principal

Status
production
Severity
medium
Group by
aws::recipientAccountId, category, dest, group.id, object_name, result, signature, src, user, vendor_product
Author
Steven Dick
Source
github.com/splunk/security_content

The following analytic detects potential privilege escalation threats in Azure Active Directory (AD). This detection is important because it identifies instances where privileged roles that hold elevated permissions are assigned to service principals. This prevents unauthorized access or malicious activities, which occur when these non-human entities access Azure resources to exploit them. False positives might occur since administrators can legitimately assign privileged roles to service principals. This detection leverages the O365 Universal Audit Log data source.

Known false positives

  • Administrators may legitimately assign the privileged roles to Service Principals as part of administrative tasks. Filter as needed.

MITRE ATT&CK coverage

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

name: O365 Privileged Role Assigned To Service Principal
id: 80f3fc1b-705f-4080-bf08-f61bf013b900
version: 11
creation_date: '2024-04-13'
modification_date: '2026-05-13'
author: Steven Dick
status: production
type: TTP
description: The following analytic detects potential privilege escalation threats in Azure Active Directory (AD). This detection is important because it identifies instances where privileged roles that hold elevated permissions are assigned to service principals. This prevents unauthorized access or malicious activities, which occur when these non-human entities access Azure resources to exploit them. False positives might occur since administrators can legitimately assign privileged roles to service principals. This detection leverages the O365 Universal Audit Log data source.
data_source:
    - Office 365 Universal Audit Log
search: "`o365_management_activity` Workload=AzureActiveDirectory Operation IN (\"Add member to role.\",\"Add eligible member to role.\") | eval user = ObjectId, src_user = case(match(mvindex('Actor{}.ID',-1),\"User\"),mvindex('Actor{}.ID',0),match(mvindex('Actor{}.ID',-1),\"ServicePrincipal\"),mvindex('Actor{}.ID',3),true(),mvindex('Actor{}.ID',0)), object_name = mvindex('ModifiedProperties{}.NewValue', mvfind('ModifiedProperties{}.Name',\"Role\\.DisplayName\")), object_id = mvindex('ModifiedProperties{}.NewValue', mvfind('ModifiedProperties{}.Name',\"Role\\.TemplateId\")), signature = Operation, result = ResultStatus, category = mvindex('Target{}.ID',2) | fillnull | stats count, min(_time) as firstTime, max(_time) as lastTime by src_user, src, user, category, result, object_name, object_id, signature,vendor_account, vendor_product, dest | lookup privileged_azure_ad_roles azuretemplateid as object_id OUTPUT isprvilegedadrole | search isprvilegedadrole=\"TRUE\" category!=\"User\" | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `o365_privileged_role_assigned_to_service_principal_filter`"
how_to_implement: You must install the Splunk Microsoft Office 365 Add-on and ingest Office 365 management activity events.
known_false_positives: Administrators may legitimately assign the privileged roles to Service Principals as part of administrative tasks. Filter as needed.
references:
    - https://attack.mitre.org/techniques/T1098/003/
    - https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference
    - https://learn.microsoft.com/en-us/microsoft-365/admin/add-users/about-exchange-online-admin-role?view=o365-worldwide
    - https://posts.specterops.io/azure-privilege-escalation-via-service-principal-abuse-210ae2be2a5
finding:
    title: A privileged Azure AD role [$object_name$] was assigned to the Service Principal $user$ initiated by $src_user$
    entity:
        field: src_user
        type: user
        score: 50
intermediate_findings:
    entities:
        - field: user
          type: user
          score: 50
          message: A privileged Azure AD role [$object_name$] was assigned to the Service Principal $user$ initiated by $src_user$
analytic_story:
    - Azure Active Directory Privilege Escalation
    - Scattered Lapsus$ Hunters
asset_type: O365 Tenant
mitre_attack_id:
    - T1098.003
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: cloud
security_domain: identity

Stages and Predicates

Stage 1: search

`o365_management_activity` Workload=AzureActiveDirectory Operation IN ("Add member to role.","Add eligible member to role.")

Stage 2: eval

| eval user = ObjectId, src_user = case(match(mvindex('Actor{}.ID',-1),"User"),mvindex('Actor{}.ID',0),match(mvindex('Actor{}.ID',-1),"ServicePrincipal"),mvindex('Actor{}.ID',3),true(),mvindex('Actor{}.ID',0)), object_name = mvindex('ModifiedProperties{}.NewValue', mvfind('ModifiedProperties{}.Name',"Role\.DisplayName")), object_id = mvindex('ModifiedProperties{}.NewValue', mvfind('ModifiedProperties{}.Name',"Role\.TemplateId")), signature = Operation, result = ResultStatus, category = mvindex('Target{}.ID',2)
src_user =
ifmatch(mvindex('Actor{}.ID', -1), "User")mvindex('Actor{}.ID', 0)
elifmatch(mvindex('Actor{}.ID', -1), "ServicePrincipal")mvindex('Actor{}.ID', 3)
elsemvindex('Actor{}.ID', 0)

Stage 3: fillnull

| fillnull

Stage 4: stats

| stats count, min(_time) as firstTime, max(_time) as lastTime by src_user, src, user, category, result, object_name, object_id, signature,vendor_account, vendor_product, dest

Stage 5: lookup

| lookup privileged_azure_ad_roles azuretemplateid as object_id OUTPUT isprvilegedadrole
Lookup table
privileged_azure_ad_roles
Key field
azuretemplateid as object_id
Output columns
['isprvilegedadrole', 'isprvilegedadrole']

Stage 6: search

| search isprvilegedadrole="TRUE" category!="User"

Stage 7: search

| `security_content_ctime(firstTime)`

Stage 8: search

| `security_content_ctime(lastTime)`

Stage 9: search

| `o365_privileged_role_assigned_to_service_principal_filter`

Indicators

These rows show field, operator, and value matches.