Detection rules › Kusto
Azure Diagnostic settings removed from a resource
This query looks for diagnostic settings that are removed from a resource. This could indicate an attacker or malicious internal trying to evade detection before malicious act is performed. If the diagnostic settings are being deleted as part of a parent resource deletion, the event is ignores.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Stealth | T1562.008 Impair Defenses: Disable or Modify Cloud Logs |
Event coverage
Rule body kusto
id: 6e95aef3-a1e0-4063-8e74-cd59aa59f245
name: Azure Diagnostic settings removed from a resource
description: |
'This query looks for diagnostic settings that are removed from a resource.
This could indicate an attacker or malicious internal trying to evade detection before malicious act is performed.
If the diagnostic settings are being deleted as part of a parent resource deletion, the event is ignores.'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActivity
dataTypes:
- AzureActivity
queryFrequency: 1h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562.008
query: |
AzureActivity
| where OperationNameValue =~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE"
| summarize
TimeGenerated = arg_max(TimeGenerated, Properties),
ActivityStatusValue = make_set(ActivityStatusValue, 5),
take_any(Caller, CallerIpAddress, OperationName, ResourceGroup, Resource)
by CorrelationId, _ResourceId, OperationNameValue
| extend ResourceHierarchy = split(_ResourceId, "/")
| extend MonitoredResourcePath = strcat_array(array_slice(ResourceHierarchy, 0, array_length(ResourceHierarchy)-5), "/")
| join kind=leftanti (
AzureActivity
| where OperationNameValue !~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE" and OperationNameValue endswith "/DELETE" and ActivityStatusValue has_any ("Success", "Succeeded")
| project _ResourceId
) on $left.MonitoredResourcePath == $right._ResourceId
| extend
Name = iif(Caller has "@", tostring(split(Caller, "@")[0]), ""),
UPNSuffix = iif(Caller has "@", tostring(split(Caller, "@")[1]), ""),
AadUserId = iif(Caller has "@", "", Caller)
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, OperationName, ActivityStatusValue, ResourceGroup, MonitoredResourcePath, Resource, Properties, Name, UPNSuffix, AadUserId, _ResourceId, CorrelationId
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Caller
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: AadUserId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CallerIpAddress
version: 1.0.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: KennethMLdk
support:
tier: Community
categories:
domains: [ "Security - Others", "Platform" ]
Stages and Predicates
Stage 1: source
AzureActivity
Stage 2: where
| where OperationNameValue =~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE"
Stage 3: summarize
| summarize
TimeGenerated = arg_max(TimeGenerated, Properties),
ActivityStatusValue = make_set(ActivityStatusValue, 5),
take_any(Caller, CallerIpAddress, OperationName, ResourceGroup, Resource)
by CorrelationId, _ResourceId, OperationNameValue
Stage 4: extend
| extend ResourceHierarchy = split(_ResourceId, "/")
Stage 5: extend
| extend MonitoredResourcePath = strcat_array(array_slice(ResourceHierarchy, 0, array_length(ResourceHierarchy)-5), "/")
Stage 6: join (negated)
| join kind=leftanti (
AzureActivity
| where OperationNameValue !~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE" and OperationNameValue endswith "/DELETE" and ActivityStatusValue has_any ("Success", "Succeeded")
| project _ResourceId
) on $left.MonitoredResourcePath == $right._ResourceId
Stage 7: extend
| extend
Name = iif(Caller has "@", tostring(split(Caller, "@")[0]), ""),
UPNSuffix = iif(Caller has "@", tostring(split(Caller, "@")[1]), ""),
AadUserId = iif(Caller has "@", "", Caller)
Stage 8: project
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, OperationName, ActivityStatusValue, ResourceGroup, MonitoredResourcePath, Resource, Properties, Name, UPNSuffix, AadUserId, _ResourceId, CorrelationId
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
ActivityStatusValue | match | Success, Succeeded |
OperationNameValue | ends_with | /DELETE |
OperationNameValue | ne | MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE |
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.
| Field | Kind | Values |
|---|---|---|
OperationNameValue | eq |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
AadUserId | project |
ActivityStatusValue | project |
Caller | project |
CallerIpAddress | project |
CorrelationId | project |
MonitoredResourcePath | project |
Name | project |
OperationName | project |
OperationNameValue | project |
Properties | project |
Resource | project |
ResourceGroup | project |
TimeGenerated | project |
UPNSuffix | project |
_ResourceId | project |