Compromised service principals may be added to privileged roles to escalate permissions and maintain persistence within the environment. SOC teams should proactively hunt for this behavior to identify potential adversary access and prevent further lateral movement or privilege escalation.
KQL Query
let queryTime = 1d;
CloudAppEvents
| where Timestamp > ago(queryTime)
| where Application == "Office 365"
| where ActionType == "Add member to role."
| extend EntityType = RawEventData.Target[2].ID, RoleName = RawEventData.ModifiedProperties[1].NewValue, RoleId = RawEventData.ModifiedProperties[2].NewValue
| where EntityType == "ServicePrincipal"
| project Timestamp , ActionType, ServicePrincipalName = RawEventData.Target[3].ID, ServicePrincipalId = RawEventData.Target[1].ID, RoleName, RoleId, ActorId = AccountObjectId , ActorDisplayName = AccountDisplayName
id: ca7c93e0-49d3-44ff-b07e-ae117ba13c9a
name: ServicePrincipalAddedToRole [Nobelium]
description: |
One of the indicators of compromise for the Nobelium (formerly Solorigate) campaign was that unexpected service principals have been added to privileged roles. This query looks for service principals that have been added to any role.
See Understanding "Solorigate"'s Identity IOCs - for Identity Vendors and their customers..
Reference - https://techcommunity.microsoft.com/t5/azure-active-directory-identity/understanding-quot-solorigate-quot-s-identity-iocs-for-identity/ba-p/2007610
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- CloudAppEvents
tactics:
- Privilege escalation
tags:
- Nobelium
query: |
let queryTime = 1d;
CloudAppEvents
| where Timestamp > ago(queryTime)
| where Application == "Office 365"
| where ActionType == "Add member to role."
| extend EntityType = RawEventData.Target[2].ID, RoleName = RawEventData.ModifiedProperties[1].NewValue, RoleId = RawEventData.ModifiedProperties[2].NewValue
| where EntityType == "ServicePrincipal"
| project Timestamp , ActionType, ServicePrincipalName = RawEventData.Target[3].ID, ServicePrincipalId = RawEventData.Target[1].ID, RoleName, RoleId, ActorId = AccountObjectId , ActorDisplayName = AccountDisplayName
| Sentinel Table | Notes |
|---|---|
CloudAppEvents | Ensure this data connector is enabled |
Scenario: Scheduled Job Configuration
Description: A legitimate scheduled job (e.g., SQL Server Agent Job, Windows Task Scheduler job) is configured to add a service principal to a role as part of an automated administrative task.
Filter/Exclusion: where (process.name != "sqlservr.exe" and process.name != "schtasks.exe") or (process.name == "sqlservr.exe" and process.command_line contains "add role")
Scenario: Azure Active Directory Role Assignment via PowerShell
Description: An admin uses PowerShell (e.g., AzureAD module) to assign a service principal to a role as part of routine identity management.
Filter/Exclusion: where (process.name != "powershell.exe" or process.command_line contains "Add-AzureADServicePrincipalToRole")
Scenario: Service Principal Creation for Automation Tool
Description: A service principal is created for a legitimate automation tool (e.g., Azure DevOps, Ansible, Puppet) and then assigned to a role for orchestration purposes.
Filter/Exclusion: where (process.name != "azure-cli.exe" and process.name != "ansible.exe") or (process.name == "azure-cli.exe" and process.command_line contains "az ad sp role assignment create")
Scenario: Role Assignment for Monitoring Tool Integration
Description: A monitoring tool (e.g., Datadog, New Relic, Splunk) is integrated with Azure AD and requires a service principal to be assigned to a role for data collection.
Filter/Exclusion: `where (process.name != “datadog-agent.exe” and process.name != “splunkforwarder”) or (process.name == “datadog-agent.exe” and process.command_line contains “datadog-integration