← Back to SOC feed Coverage →

NewAppOrServicePrincipalCredential[Nobelium]

kql MEDIUM Azure-Sentinel
CloudAppEvents
aptbackdoorcredential-thefthuntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-23T23:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may add new credentials to applications or service principals to maintain persistence and escalate privileges within the Azure environment. SOC teams should proactively hunt for this behavior as it aligns with known Nobelium tactics and can indicate unauthorized access or lateral movement.

KQL Query

// New access credential added to application or service principal
let auditLookback = 1d;
CloudAppEvents
| where Timestamp > ago(auditLookback)
| where ActionType in ("Add service principal.", "Add service principal credentials.", "Update application  Certificates and secrets management ")
| extend RawEventData = parse_json(RawEventData)
| where RawEventData.ResultStatus =~ "success"
// Select only users or applications initiating the credential changes
| extend ActorDetails = RawEventData.Actor
| mvexpand ActorDetails
| where ActorDetails has "@"
| extend targetDetails = parse_json(ActivityObjects[1])
| extend targetId = targetDetails.Id
| extend targetType = targetDetails.Type
| extend targetDisplayName = targetDetails.Name
| extend keyEvents = RawEventData.ModifiedProperties
| where keyEvents has "KeyIdentifier=" and keyEvents has "KeyUsage=Verify"
| mvexpand keyEvents
| where keyEvents.Name =~ "KeyDescription"
| parse keyEvents.NewValue with * "KeyIdentifier=" keyIdentifier:string ",KeyType=" keyType:string ",KeyUsage=" keyUsage:string ",DisplayName=" keyDisplayName:string "]" *
| parse keyEvents.OldValue with * "KeyIdentifier=" keyIdentifierOld:string ",KeyType" *
| where keyEvents.OldValue == "[]" or keyIdentifier != keyIdentifierOld
| where keyUsage == "Verify"
| project-away keyEvents
| project Timestamp, ActionType, InitiatingUserOrApp=AccountDisplayName, InitiatingIPAddress=IPAddress, UserAgent, targetDisplayName, targetId, targetType, keyDisplayName, keyType, keyUsage, keyIdentifier

Analytic Rule Definition

id: bf246545-504c-4bea-a6a2-3b6e7f65b202
name: NewAppOrServicePrincipalCredential[Nobelium]
description: |
  This query will find when a new credential is added to an application or service principal.
  The Nobelium activity group was able to gain sufficient access to add credentials to existing applications with mail read permissions. They used that access to exfiltrate email.
  See Customer Guidance on Recent Nation-State Cyber Attacks for more on the Nobelium campaign (formerly known as Solorigate).
  Additional information on OAuth Credential Grants can be found in RFC 6749 Section 4.4 or Microsoft Entra ID audit activity reference.
  For further information on AuditLogs please see Microsoft Entra ID audit activity reference.
  This query was inspired by an Azure Sentinel detection.
  References:
  https://msrc-blog.microsoft.com/2020/12/13/customer-guidance-on-recent-nation-state-cyber-attacks/
  https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
  https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities
  https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/NewAppOrServicePrincipalCredential.yaml
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - CloudAppEvents
tactics:
- Persistence
tags:
- Nobelium
query: |
  // New access credential added to application or service principal
  let auditLookback = 1d;
  CloudAppEvents
  | where Timestamp > ago(auditLookback)
  | where ActionType in ("Add service principal.", "Add service principal credentials.", "Update application  Certificates and secrets management ")
  | extend RawEventData = parse_json(RawEventData)
  | where RawEventData.ResultStatus =~ "success"
  // Select only users or applications initiating the credential changes
  | extend ActorDetails = RawEventData.Actor
  | mvexpand ActorDetails
  | where ActorDetails has "@"
  | extend targetDetails = parse_json(ActivityObjects[1])
  | extend targetId = targetDetails.Id
  | extend targetType = targetDetails.Type
  | extend targetDisplayName = targetDetails.Name
  | extend keyEvents = RawEventData.ModifiedProperties
  | where keyEvents has "KeyIdentifier=" and keyEvents has "KeyUsage=Verify"
  | mvexpand keyEvents
  | where keyEvents.Name =~ "KeyDescription"
  | parse keyEvents.NewValue with * "KeyIdentifier=" keyIdentifier:string ",KeyType=" keyType:string ",KeyUsage=" keyUsage:string ",DisplayName=" keyDisplayName:string "]" *
  | parse keyEvents.OldValue with * "KeyIdentifier=" keyIdentifierOld:string ",KeyType" *
  | where keyEvents.OldValue == "[]" or keyIdentifier != keyIdentifierOld
  | where keyUsage == "Verify"
  | project-away keyEvents
  | project Timestamp, ActionType, InitiatingUserOrApp=AccountDisplayName, InitiatingIPAddress=IPAddress, UserAgent, targetDisplayName, targetId, targetType, keyDisplayName, keyType, keyUsage, keyIdentifier

Required Data Sources

Sentinel TableNotes
CloudAppEventsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/Microsoft 365 Defender/Persistence/NewAppOrServicePrincipalCredential[Nobelium].yaml