Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

NRT First access credential added to Application or Service Principal where no credential was present

Back
Idb6988c32-4f3b-4a45-8313-b46b33061a74
RulenameNRT First access credential added to Application or Service Principal where no credential was present
DescriptionThis will alert when an admin or app owner account adds a new credential to an Application or Service Principal where there was no previous verify KeyCredential associated.

If a threat actor obtains access to an account with sufficient privileges and adds the alternate authentication material triggering this event, the threat actor can now authenticate as the Application or Service Principal using this credential.

Additional information on OAuth Credential Grants can be found in RFC 6749 Section 4.4 or https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow

For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.
Categories
Author
SeverityMedium
TacticsDefenseEvasion
TechniquesT1550.001
Required data connectorsAzureActiveDirectory
KindNRT
Source
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/nrt_FirstAppOrServicePrincipalCredential.yaml
Version1.0.6
Arm templateb6988c32-4f3b-4a45-8313-b46b33061a74.json
Deploy To Azure
AuditLogs
| where OperationName has_any ("Add service principal", "Certificates and secrets management")
| where Result =~ "success"
| where tostring(InitiatedBy.user.userPrincipalName) has "@" or tostring(InitiatedBy.app.displayName) has "@"
| mv-apply TargetResource = TargetResources on 
  (
    where TargetResource.type =~ "Application"
    | extend targetDisplayName = tostring(TargetResource.displayName),
             targetId = tostring(TargetResource.id),
             targetType = tostring(TargetResource.type),
             keyEvents = TargetResource.modifiedProperties
  )
|  mv-apply Property = keyEvents on 
 (
  where Property.displayName =~ "KeyDescription"
  | extend new_value_set = parse_json(tostring(Property.newValue)),
           old_value_set = parse_json(tostring(Property.oldValue))
 )
| where old_value_set == "[]"
| mv-expand new_value_set
| parse new_value_set with * "KeyIdentifier=" keyIdentifier:string ",KeyType=" keyType:string ",KeyUsage=" keyUsage:string ",DisplayName=" keyDisplayName:string "]" *
| where keyUsage == "Verify"
 | mv-apply AdditionalDetail = AdditionalDetails on 
  (
    where AdditionalDetail.key =~ "User-Agent"
    | extend UserAgent = tostring(AdditionalDetail.value)
  )
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
//| where targetType =~ "Application" // or targetType =~ "ServicePrincipal"
| project-away new_value_set, old_value_set
| project-reorder TimeGenerated, OperationName, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, UserAgent, targetDisplayName, targetId, targetType, keyDisplayName, keyType, keyUsage, keyIdentifier, CorrelationId, TenantId
| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
tactics:
- DefenseEvasion
version: 1.0.6
author:
  name: Samik Roy
query: |
  AuditLogs
  | where OperationName has_any ("Add service principal", "Certificates and secrets management")
  | where Result =~ "success"
  | where tostring(InitiatedBy.user.userPrincipalName) has "@" or tostring(InitiatedBy.app.displayName) has "@"
  | mv-apply TargetResource = TargetResources on 
    (
      where TargetResource.type =~ "Application"
      | extend targetDisplayName = tostring(TargetResource.displayName),
               targetId = tostring(TargetResource.id),
               targetType = tostring(TargetResource.type),
               keyEvents = TargetResource.modifiedProperties
    )
  |  mv-apply Property = keyEvents on 
   (
    where Property.displayName =~ "KeyDescription"
    | extend new_value_set = parse_json(tostring(Property.newValue)),
             old_value_set = parse_json(tostring(Property.oldValue))
   )
  | where old_value_set == "[]"
  | mv-expand new_value_set
  | parse new_value_set with * "KeyIdentifier=" keyIdentifier:string ",KeyType=" keyType:string ",KeyUsage=" keyUsage:string ",DisplayName=" keyDisplayName:string "]" *
  | where keyUsage == "Verify"
   | mv-apply AdditionalDetail = AdditionalDetails on 
    (
      where AdditionalDetail.key =~ "User-Agent"
      | extend UserAgent = tostring(AdditionalDetail.value)
    )
  | extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
  | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
  | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
  | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
  | extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
  //| where targetType =~ "Application" // or targetType =~ "ServicePrincipal"
  | project-away new_value_set, old_value_set
  | project-reorder TimeGenerated, OperationName, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, UserAgent, targetDisplayName, targetId, targetType, keyDisplayName, keyType, keyUsage, keyIdentifier, CorrelationId, TenantId
  | extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])  
name: NRT First access credential added to Application or Service Principal where no credential was present
source:
  kind: Community
relevantTechniques:
- T1550.001
entityMappings:
- fieldMappings:
  - columnName: InitiatingUserPrincipalName
    identifier: FullName
  - columnName: InitiatedByName
    identifier: Name
  - columnName: InitiatedByUPNSuffix
    identifier: UPNSuffix
  entityType: Account
- fieldMappings:
  - columnName: InitiatingAadUserId
    identifier: AadUserId
  entityType: Account
- fieldMappings:
  - columnName: InitiatingAppServicePrincipalId
    identifier: AadUserId
  entityType: Account
- fieldMappings:
  - columnName: InitiatingIpAddress
    identifier: Address
  entityType: IP
id: b6988c32-4f3b-4a45-8313-b46b33061a74
requiredDataConnectors:
- dataTypes:
  - AuditLogs
  connectorId: AzureActiveDirectory
tags:
- Solorigate
- NOBELIUM
severity: Medium
metadata: 
description: |
  'This will alert when an admin or app owner account adds a new credential to an Application or Service Principal where there was no previous verify KeyCredential associated.
  If a threat actor obtains access to an account with sufficient privileges and adds the alternate authentication material triggering this event, the threat actor can now authenticate as the Application or Service Principal using this credential.
  Additional information on OAuth Credential Grants can be found in RFC 6749 Section 4.4 or https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
  For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.'  
support:
  tier: Community
threatAnalysisTactics:
- DefenseEvasion
kind: NRT
categories:
  domains:
  - Security - Others
  - Identity
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/nrt_FirstAppOrServicePrincipalCredential.yaml
threatAnalysisTechniques:
- T1550.001
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/b6988c32-4f3b-4a45-8313-b46b33061a74')]",
      "kind": "NRT",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/b6988c32-4f3b-4a45-8313-b46b33061a74')]",
      "properties": {
        "alertRuleTemplateName": "b6988c32-4f3b-4a45-8313-b46b33061a74",
        "author": {
          "name": "Samik Roy"
        },
        "categories": {
          "domains": [
            "Security - Others",
            "Identity"
          ]
        },
        "customDetails": null,
        "description": "'This will alert when an admin or app owner account adds a new credential to an Application or Service Principal where there was no previous verify KeyCredential associated.\nIf a threat actor obtains access to an account with sufficient privileges and adds the alternate authentication material triggering this event, the threat actor can now authenticate as the Application or Service Principal using this credential.\nAdditional information on OAuth Credential Grants can be found in RFC 6749 Section 4.4 or https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow\nFor further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.'\n",
        "displayName": "NRT First access credential added to Application or Service Principal where no credential was present",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "InitiatingUserPrincipalName",
                "identifier": "FullName"
              },
              {
                "columnName": "InitiatedByName",
                "identifier": "Name"
              },
              {
                "columnName": "InitiatedByUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "InitiatingAadUserId",
                "identifier": "AadUserId"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "InitiatingAppServicePrincipalId",
                "identifier": "AadUserId"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "InitiatingIpAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/nrt_FirstAppOrServicePrincipalCredential.yaml",
        "query": "AuditLogs\n| where OperationName has_any (\"Add service principal\", \"Certificates and secrets management\")\n| where Result =~ \"success\"\n| where tostring(InitiatedBy.user.userPrincipalName) has \"@\" or tostring(InitiatedBy.app.displayName) has \"@\"\n| mv-apply TargetResource = TargetResources on \n  (\n    where TargetResource.type =~ \"Application\"\n    | extend targetDisplayName = tostring(TargetResource.displayName),\n             targetId = tostring(TargetResource.id),\n             targetType = tostring(TargetResource.type),\n             keyEvents = TargetResource.modifiedProperties\n  )\n|  mv-apply Property = keyEvents on \n (\n  where Property.displayName =~ \"KeyDescription\"\n  | extend new_value_set = parse_json(tostring(Property.newValue)),\n           old_value_set = parse_json(tostring(Property.oldValue))\n )\n| where old_value_set == \"[]\"\n| mv-expand new_value_set\n| parse new_value_set with * \"KeyIdentifier=\" keyIdentifier:string \",KeyType=\" keyType:string \",KeyUsage=\" keyUsage:string \",DisplayName=\" keyDisplayName:string \"]\" *\n| where keyUsage == \"Verify\"\n | mv-apply AdditionalDetail = AdditionalDetails on \n  (\n    where AdditionalDetail.key =~ \"User-Agent\"\n    | extend UserAgent = tostring(AdditionalDetail.value)\n  )\n| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)\n| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)\n| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)\n| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)\n| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))\n//| where targetType =~ \"Application\" // or targetType =~ \"ServicePrincipal\"\n| project-away new_value_set, old_value_set\n| project-reorder TimeGenerated, OperationName, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, UserAgent, targetDisplayName, targetId, targetType, keyDisplayName, keyType, keyUsage, keyIdentifier, CorrelationId, TenantId\n| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])\n",
        "severity": "Medium",
        "source": {
          "kind": "Community"
        },
        "subTechniques": [
          "T1550.001"
        ],
        "support": {
          "tier": "Community"
        },
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "DefenseEvasion"
        ],
        "tags": [
          "Solorigate",
          "NOBELIUM"
        ],
        "techniques": [
          "T1550"
        ],
        "templateVersion": "1.0.6",
        "threatAnalysisTactics": [
          "DefenseEvasion"
        ],
        "threatAnalysisTechniques": [
          "T1550.001"
        ]
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}