Detection rules › Splunk

O365 Admin Consent Bypassed by Service Principal

Status
production
Severity
medium
Group by
aws::recipientAccountId, dest, dest_user, roleDescription, roleId, roleValue, signature, src, user, vendor_product
Author
Mauricio Velazco, Splunk
Source
github.com/splunk/security_content

The following analytic identifies instances where a service principal in Office 365 Azure Active Directory assigns app roles without standard admin consent. It leverages o365_management_activity logs, specifically focusing on the 'Add app role assignment to service principal' operation. This activity is significant for SOCs as it may indicate a bypass of critical administrative controls, potentially leading to unauthorized access or privilege escalation. If confirmed malicious, this could allow an attacker to misuse automated processes to assign sensitive permissions, compromising the security of the environment.

MITRE ATT&CK coverage

Rule body splunk

name: O365 Admin Consent Bypassed by Service Principal
id: 8a1b22eb-50ce-4e26-a691-97ff52349569
version: 9
creation_date: '2024-02-14'
modification_date: '2026-05-13'
author: Mauricio Velazco, Splunk
status: production
type: TTP
description: The following analytic identifies instances where a service principal in Office 365 Azure Active Directory assigns app roles without standard admin consent. It leverages `o365_management_activity` logs, specifically focusing on the 'Add app role assignment to service principal' operation. This activity is significant for SOCs as it may indicate a bypass of critical administrative controls, potentially leading to unauthorized access or privilege escalation. If confirmed malicious, this could allow an attacker to misuse automated processes to assign sensitive permissions, compromising the security of the environment.
data_source:
    - O365 Add app role assignment to service principal.
search: "`o365_management_activity` Workload=AzureActiveDirectory Operation=\"Add app role assignment to service principal.\" | eval len=mvcount('Actor{}.ID') | eval userType = mvindex('Actor{}.ID',len-1) | eval roleId = mvindex('ModifiedProperties{}.NewValue', 0) | eval roleValue = mvindex('ModifiedProperties{}.NewValue', 1) | eval roleDescription = mvindex('ModifiedProperties{}.NewValue', 2) | eval dest_user = mvindex('Target{}.ID', 0) | search userType = \"ServicePrincipal\" | eval src_user = user | fillnull | stats count min(_time) as firstTime max(_time) as lastTime by signature dest user src vendor_account vendor_product dest_user roleId roleValue roleDescription | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `o365_admin_consent_bypassed_by_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: Service Principals are sometimes configured to legitimately bypass the consent process for purposes of automation. Filter as needed.
references:
    - https://attack.mitre.org/techniques/T1098/003/
    - https://msrc.microsoft.com/blog/2024/01/microsoft-actions-following-attack-by-nation-state-actor-midnight-blizzard/
    - https://www.microsoft.com/en-us/security/blog/2024/01/25/midnight-blizzard-guidance-for-responders-on-nation-state-attack/
    - https://attack.mitre.org/techniques/T1098/002/
    - https://www.microsoft.com/en-us/security/blog/2024/01/25/midnight-blizzard-guidance-for-responders-on-nation-state-attack/
    - https://winsmarts.com/how-to-grant-admin-consent-to-an-api-programmatically-e32f4a100e9d
drilldown_searches:
    - name: View the detection results for - "$dest_user$"
      search: '%original_detection_search% | search  dest_user = "$dest_user$"'
      earliest_offset: $info_min_time$
      latest_offset: $info_max_time$
    - name: View risk events for the last 7 days for - "$dest_user$"
      search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$dest_user$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`'
      earliest_offset: 7d
      latest_offset: "0"
finding:
    title: Service principal $user$ bypassed the admin consent process and granted permissions to $dest_user$
    entity:
        field: dest_user
        type: user
        score: 50
analytic_story:
    - Office 365 Persistence Mechanisms
asset_type: O365 Tenant
mitre_attack_id:
    - T1098.003
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: cloud
security_domain: identity
tests:
    - name: True Positive Test
      attack_data:
        - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1098.003/o365_bypass_admin_consent/o365_bypass_admin_consent.log
          source: o365
          sourcetype: o365:management:activity
      test_type: unit

Stages and Predicates

Stage 1: search

`o365_management_activity` Workload=AzureActiveDirectory Operation="Add app role assignment to service principal."

Stage 2: eval

| eval len=mvcount('Actor{}.ID')

Stage 3: eval

| eval userType = mvindex('Actor{}.ID',len-1)

Stage 4: eval

| eval roleId = mvindex('ModifiedProperties{}.NewValue', 0)

Stage 5: eval

| eval roleValue = mvindex('ModifiedProperties{}.NewValue', 1)

Stage 6: eval

| eval roleDescription = mvindex('ModifiedProperties{}.NewValue', 2)

Stage 7: eval

| eval dest_user = mvindex('Target{}.ID', 0)

Stage 8: search

| search userType = "ServicePrincipal"

Stage 9: eval

| eval src_user = user

Stage 10: fillnull

| fillnull

Stage 11: stats

| stats count min(_time) as firstTime max(_time) as lastTime by signature dest user src vendor_account vendor_product dest_user roleId roleValue roleDescription

Stage 12: search

| `security_content_ctime(firstTime)`

Stage 13: search

| `security_content_ctime(lastTime)`

Stage 14: search

| `o365_admin_consent_bypassed_by_service_principal_filter`

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
Operationeq
  • "Add app role assignment to service principal."
Workloadeq
  • AzureActiveDirectory
sourcetypeeq
  • o365:management:activity
userTypeeq
  • "ServicePrincipal"