Query Details

Audit Logs App Or Service Principal Credential

Query

// This rule detects when the authentication credentials for an Application or Service Principal are modified. If a threat actor obtains access to an account with sufficient privileges and adds an alternate authentication credential, the threat actor can now authenticate as the Application or Service Principal.
// 
// Ref: https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
// Ref: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.
let _BenignApps = toscalar(
    _GetWatchlist('Activity-ExpectedSignificantActivity')
    | where Activity == "AppServiceCredentialModification" and Notes has "[App]"
    | summarize make_list(ActorId)
    );
let query_frequency = 1h;
let query_period = 1d;
AuditLogs
| where TimeGenerated > ago(query_period)
// Capture "Add application", "Add service principal", "Add service principal credentials", and "Update application - Certificates and secrets management" events
| where OperationName has_any ("Add application", "Add service principal", "Certificates and secrets management")
| where Result == "success"
| mv-apply ModifiedProperty = TargetResources[0].modifiedProperties on (
    summarize BagToUnpack = make_bag(pack(tostring(ModifiedProperty.displayName), pack("oldValue", ModifiedProperty.oldValue, "newValue", ModifiedProperty.newValue))))
| evaluate bag_unpack(BagToUnpack, columnsConflict='replace_source')
| extend
    // Information about the actor
    ActorIdentity = Identity,
    // Information about the actor, if it was a user
    ActorUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
    ActorUserIPAddress = tostring(InitiatedBy.user.ipAddress),
    //ActorUserRoles = tostring(InitiatedBy.user.roles),
    ActorUserId = tostring(InitiatedBy.user.id),
    // Information about the actor, if it was an application
    ActorAppName = tostring(InitiatedBy.app.displayName),
    ActorAppId = tostring(InitiatedBy.app.appId),
    ActorAppServicePrincipalName = tostring(InitiatedBy.app.servicePrincipalName),
    ActorAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
    // Information about the target object
    TargetType = tostring(TargetResources[0].type),
    TargetDisplayName = tostring(TargetResources[0].displayName),
    TargetId = tostring(TargetResources[0].id)
| as _KeyCredentialEvents
| join kind=leftouter (
    _KeyCredentialEvents
    | summarize OperationNames = make_set(OperationName) by TargetId
    | extend EventType = iff(OperationNames has "Add application", "First access credential added to", "")
    | project EventType, TargetId
    )
    on TargetId
| where not(OperationName == "Add application")
| extend KeyDescription = column_ifexists("KeyDescription", dynamic([]))
| where isnotempty(KeyDescription)
| as _KeyCredentialEventsExtended
| where TimeGenerated > ago(query_frequency)
| where not(ActorAppServicePrincipalId in (_BenignApps))
| join kind=leftouter (
    _KeyCredentialEventsExtended
    | summarize arg_min(TimeGenerated, CorrelationId) by TargetId
    | project MinCorrelationId = CorrelationId, TargetId
    )
    on TargetId
| extend AddedCredentials = set_difference(todynamic(tostring(KeyDescription.newValue)), todynamic(tostring(KeyDescription.oldValue)))
| extend EventType = iff(MinCorrelationId == CorrelationId and isnotempty(EventType),
    EventType,
    case(
        OperationName == "Add service principal", "First access credential added to",
        array_length(AddedCredentials) == 0, "Removed access credential from",
        KeyDescription.oldValue == "[]", "Renewed access credential added to",
        "Alternative access credential added to"
    ))
| mv-expand AddedCredentials = iff(array_length(AddedCredentials) != 0, AddedCredentials, dynamic([""])) to typeof(string)
| parse AddedCredentials with "[KeyIdentifier=" KeyIdentifier: string ",KeyType=" KeyType: string ",KeyUsage=" KeyUsage: string ",DisplayName=" KeyDisplayName: string "]"
| extend
    AlertName = strcat(EventType, " Application or Service Principal"),
    AlertSeverity = case(
        not(IsWorkingTime(TimeGenerated)) and isnotempty(ActorUserPrincipalName), "High",
        EventType startswith "First", "High",
        "Medium"
    )
| project
    TimeGenerated,
    OperationName,
    ActorIdentity,
    ActorUserPrincipalName,
    ActorAppName,
    TargetType,
    TargetDisplayName,
    KeyDisplayName,
    KeyType,
    KeyUsage,
    KeyIdentifier,
    KeyDescription,
    ActorUserIPAddress,
    ActorUserId,
    ActorAppId,
    ActorAppServicePrincipalName,
    ActorAppServicePrincipalId,
    TargetId,
    InitiatedBy,
    TargetResources,
    AdditionalDetails,
    CorrelationId,
    AlertName,
    AlertSeverity

Explanation

This query detects when the authentication credentials for an Application or Service Principal are modified. It looks for events such as adding an application, adding a service principal, managing certificates and secrets, etc. It filters for successful events and extracts information about the actor and the target object. It then joins the events with additional information and identifies the type of event (e.g., first access credential added, removed access credential, renewed access credential, alternative access credential added). Finally, it creates alerts with relevant information such as the time of the event, operation name, actor information, target information, key details, and alert severity.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: January 3, 2023

Tables

AuditLogs

Keywords

Authentication,Application,ServicePrincipal,ThreatActor,Account,Privileges,AlternateAuthenticationCredential,AzureActiveDirectory,OAuth2ClientCredentialsGrantFlow,ReportsMonitoring,Activity,AppServiceCredentialModification,Addapplication,Addserviceprincipal,Certificatesandsecretsmanagement,Success,ModifiedProperty,ActorId,Identity,InitiatedBy,TargetResources,KeyDescription,TimeGenerated,OperationName,ActorIdentity,ActorUserPrincipalName,ActorUserIPAddress,ActorUserId,ActorAppName,ActorAppId,ActorAppServicePrincipalName,ActorAppServicePrincipalId,TargetType,TargetDisplayName,TargetId,EventType,KeyIdentifier,KeyType,KeyUsage,KeyDisplayName,AddedCredentials,IsWorkingTime,AlertName,AlertSeverity

Operators

toscalar_GetWatchlistwheresummarizemake_listquery_frequencyquery_periodAuditLogsmv-applyonevaluateextendasjoincolumn_ifexistsisnotemptyset_differencetodynamiciffarray_lengthmv-expandparsestrcatcaseproject

Actions