Credential added after admin consented to Application
Id | 707494a5-8e44-486b-90f8-155d1797a8eb |
Rulename | Credential added after admin consented to Application |
Description | This query will identify instances where Service Principal credentials were added to an application by one user after the application was granted admin consent rights by another user. 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 |
Severity | Medium |
Tactics | CredentialAccess |
Required data connectors | AzureActiveDirectory |
Kind | Scheduled |
Query frequency | 1d |
Query period | 2d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/CredentialAddedAfterAdminConsent.yaml |
Version | 1.0.2 |
Arm template | 707494a5-8e44-486b-90f8-155d1797a8eb.json |
let auditLookbackStart = 2d;
let auditLookbackEnd = 1d;
AuditLogs
| where TimeGenerated >= ago(auditLookbackStart)
| where OperationName =~ "Consent to application"
| where Result =~ "success"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "ServicePrincipal"
| extend targetResourceName = tostring(TargetResource.displayName),
targetResourceID = tostring(TargetResource.id),
targetResourceType = tostring(TargetResource.type),
targetModifiedProp = TargetResource.modifiedProperties
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "ConsentContext.IsAdminConsent"
| extend isAdminConsent = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "ConsentAction.Permissions"
| extend Consent_Permissions = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "TargetId.ServicePrincipalNames"
| extend Consent_ServicePrincipalNames = tostring(extract_all(@"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",trim(@'"',tostring(Property.newValue)))[0])
)
| extend Consent_InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend Consent_InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))
| join (
AuditLogs
| where TimeGenerated >= ago(auditLookbackEnd)
| where OperationName =~ "Add service principal credentials"
| where Result =~ "success"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "ServicePrincipal"
| extend targetResourceName = tostring(TargetResource.displayName),
targetResourceID = tostring(TargetResource.id),
targetModifiedProp = TargetResource.modifiedProperties
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "KeyDescription"
| extend Credential_KeyDescription = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "TargetId.ServicePrincipalNames"
| extend Credential_ServicePrincipalNames = tostring(extract_all(@"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",trim(@'"',tostring(Property.newValue)))[0])
)
| extend Credential_InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend Credential_InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))
) on targetResourceName, targetResourceID
| extend TimeConsent = TimeGenerated, TimeCred = TimeGenerated1
| where TimeConsent < TimeCred
| project TimeConsent, TimeCred, Consent_InitiatingUserOrApp, Credential_InitiatingUserOrApp, targetResourceName, targetResourceType, isAdminConsent, Consent_ServicePrincipalNames, Credential_ServicePrincipalNames, Consent_Permissions, Credential_KeyDescription, Consent_InitiatingIpAddress, Credential_InitiatingIpAddress
| extend timestamp = TimeConsent, Name = tostring(split(Credential_InitiatingUserOrApp,'@',0)[0]), UPNSuffix = tostring(split(Credential_InitiatingUserOrApp,'@',1)[0])
kind: Scheduled
triggerOperator: gt
queryFrequency: 1d
name: Credential added after admin consented to Application
tags:
- Solorigate
- NOBELIUM
queryPeriod: 2d
id: 707494a5-8e44-486b-90f8-155d1797a8eb
description: |
'This query will identify instances where Service Principal credentials were added to an application by one user after the application was granted admin consent rights by another user.
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'
severity: Medium
query: |
let auditLookbackStart = 2d;
let auditLookbackEnd = 1d;
AuditLogs
| where TimeGenerated >= ago(auditLookbackStart)
| where OperationName =~ "Consent to application"
| where Result =~ "success"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "ServicePrincipal"
| extend targetResourceName = tostring(TargetResource.displayName),
targetResourceID = tostring(TargetResource.id),
targetResourceType = tostring(TargetResource.type),
targetModifiedProp = TargetResource.modifiedProperties
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "ConsentContext.IsAdminConsent"
| extend isAdminConsent = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "ConsentAction.Permissions"
| extend Consent_Permissions = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "TargetId.ServicePrincipalNames"
| extend Consent_ServicePrincipalNames = tostring(extract_all(@"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",trim(@'"',tostring(Property.newValue)))[0])
)
| extend Consent_InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend Consent_InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))
| join (
AuditLogs
| where TimeGenerated >= ago(auditLookbackEnd)
| where OperationName =~ "Add service principal credentials"
| where Result =~ "success"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "ServicePrincipal"
| extend targetResourceName = tostring(TargetResource.displayName),
targetResourceID = tostring(TargetResource.id),
targetModifiedProp = TargetResource.modifiedProperties
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "KeyDescription"
| extend Credential_KeyDescription = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "Included Updated Properties"
| extend UpdatedProperties = trim(@'"',tostring(Property.newValue))
)
| mv-apply Property = targetModifiedProp on
(
where Property.displayName =~ "TargetId.ServicePrincipalNames"
| extend Credential_ServicePrincipalNames = tostring(extract_all(@"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",trim(@'"',tostring(Property.newValue)))[0])
)
| extend Credential_InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend Credential_InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))
) on targetResourceName, targetResourceID
| extend TimeConsent = TimeGenerated, TimeCred = TimeGenerated1
| where TimeConsent < TimeCred
| project TimeConsent, TimeCred, Consent_InitiatingUserOrApp, Credential_InitiatingUserOrApp, targetResourceName, targetResourceType, isAdminConsent, Consent_ServicePrincipalNames, Credential_ServicePrincipalNames, Consent_Permissions, Credential_KeyDescription, Consent_InitiatingIpAddress, Credential_InitiatingIpAddress
| extend timestamp = TimeConsent, Name = tostring(split(Credential_InitiatingUserOrApp,'@',0)[0]), UPNSuffix = tostring(split(Credential_InitiatingUserOrApp,'@',1)[0])
triggerThreshold: 0
version: 1.0.2
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/CredentialAddedAfterAdminConsent.yaml
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Consent_InitiatingIpAddress
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- CredentialAccess
status: Available
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/707494a5-8e44-486b-90f8-155d1797a8eb')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/707494a5-8e44-486b-90f8-155d1797a8eb')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "Scheduled",
"apiVersion": "2022-11-01-preview",
"properties": {
"displayName": "Credential added after admin consented to Application",
"description": "'This query will identify instances where Service Principal credentials were added to an application by one user after the application was granted admin consent rights by another user.\n 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.\n 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.\n For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities'\n",
"severity": "Medium",
"enabled": true,
"query": "let auditLookbackStart = 2d;\nlet auditLookbackEnd = 1d;\nAuditLogs\n| where TimeGenerated >= ago(auditLookbackStart)\n| where OperationName =~ \"Consent to application\" \n| where Result =~ \"success\"\n| mv-apply TargetResource = TargetResources on \n (\n where TargetResource.type =~ \"ServicePrincipal\"\n | extend targetResourceName = tostring(TargetResource.displayName),\n targetResourceID = tostring(TargetResource.id),\n targetResourceType = tostring(TargetResource.type),\n targetModifiedProp = TargetResource.modifiedProperties\n )\n| mv-apply Property = targetModifiedProp on \n (\n where Property.displayName =~ \"ConsentContext.IsAdminConsent\"\n | extend isAdminConsent = trim(@'\"',tostring(Property.newValue))\n )\n| mv-apply Property = targetModifiedProp on \n (\n where Property.displayName =~ \"ConsentAction.Permissions\"\n | extend Consent_Permissions = trim(@'\"',tostring(Property.newValue))\n )\n| mv-apply Property = targetModifiedProp on \n (\n where Property.displayName =~ \"TargetId.ServicePrincipalNames\"\n | extend Consent_ServicePrincipalNames = tostring(extract_all(@\"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\",trim(@'\"',tostring(Property.newValue)))[0])\n )\n| extend Consent_InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))\n| extend Consent_InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))\n| join ( \nAuditLogs\n| where TimeGenerated >= ago(auditLookbackEnd)\n| where OperationName =~ \"Add service principal credentials\"\n| where Result =~ \"success\"\n| mv-apply TargetResource = TargetResources on \n (\n where TargetResource.type =~ \"ServicePrincipal\"\n | extend targetResourceName = tostring(TargetResource.displayName),\n targetResourceID = tostring(TargetResource.id),\n targetModifiedProp = TargetResource.modifiedProperties\n )\n| mv-apply Property = targetModifiedProp on \n (\n where Property.displayName =~ \"KeyDescription\"\n | extend Credential_KeyDescription = trim(@'\"',tostring(Property.newValue))\n )\n| mv-apply Property = targetModifiedProp on \n (\n where Property.displayName =~ \"Included Updated Properties\"\n | extend UpdatedProperties = trim(@'\"',tostring(Property.newValue))\n )\n| mv-apply Property = targetModifiedProp on \n (\n where Property.displayName =~ \"TargetId.ServicePrincipalNames\"\n | extend Credential_ServicePrincipalNames = tostring(extract_all(@\"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\",trim(@'\"',tostring(Property.newValue)))[0])\n )\n| extend Credential_InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))\n| extend Credential_InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))\n) on targetResourceName, targetResourceID\n| extend TimeConsent = TimeGenerated, TimeCred = TimeGenerated1\n| where TimeConsent < TimeCred \n| project TimeConsent, TimeCred, Consent_InitiatingUserOrApp, Credential_InitiatingUserOrApp, targetResourceName, targetResourceType, isAdminConsent, Consent_ServicePrincipalNames, Credential_ServicePrincipalNames, Consent_Permissions, Credential_KeyDescription, Consent_InitiatingIpAddress, Credential_InitiatingIpAddress\n| extend timestamp = TimeConsent, Name = tostring(split(Credential_InitiatingUserOrApp,'@',0)[0]), UPNSuffix = tostring(split(Credential_InitiatingUserOrApp,'@',1)[0])\n",
"queryFrequency": "P1D",
"queryPeriod": "P2D",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"CredentialAccess"
],
"alertRuleTemplateName": "707494a5-8e44-486b-90f8-155d1797a8eb",
"customDetails": null,
"entityMappings": [
{
"fieldMappings": [
{
"identifier": "Name",
"columnName": "Name"
},
{
"identifier": "UPNSuffix",
"columnName": "UPNSuffix"
}
],
"entityType": "Account"
},
{
"fieldMappings": [
{
"identifier": "Address",
"columnName": "Consent_InitiatingIpAddress"
}
],
"entityType": "IP"
}
],
"templateVersion": "1.0.2",
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/CredentialAddedAfterAdminConsent.yaml",
"tags": [
"Solorigate",
"NOBELIUM"
],
"status": "Available"
}
}
]
}