Detection rules › Kusto

Exchange AuditLog Disabled

Status
available
Severity
medium
Time window
1d
Group by
AdminAuditLogEnabledValue, ClientIP, Operation, Parameters, ResultStatus, UserId, UserType
Source
github.com/Azure/Azure-Sentinel

Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.

MITRE ATT&CK coverage

TacticTechniques
StealthT1562 Impair Defenses

Event coverage

Rule body kusto

id: 194dd92e-d6e7-4249-85a5-273350a7f5ce
name: Exchange AuditLog Disabled
description: |
  'Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
      - OfficeActivity (Exchange)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562
query: |
  OfficeActivity
  | where OfficeWorkload =~ "Exchange" 
  | where UserType in~ ("Admin","DcAdmin")
  // Only admin or global-admin can disable audit logging
  | where Operation =~ "Set-AdminAuditLogConfig"
  | extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(Parameters),3,3)))[0])).Value)
  | where AdminAuditLogEnabledValue =~ "False"
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters, AdminAuditLogEnabledValue
  | extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)
  | extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')
  | extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName)
  | extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '')
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserId
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountNTDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
version: 2.0.6
kind: Scheduled

Stages and Predicates

Stage 1: source

OfficeActivity

Stage 2: where

| where OfficeWorkload =~ "Exchange"

Stage 3: where

| where UserType in~ ("Admin","DcAdmin")

Stage 4: where

| where Operation =~ "Set-AdminAuditLogConfig"

Stage 5: extend

| extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(Parameters),3,3)))[0])).Value)

Stage 6: where

| where AdminAuditLogEnabledValue =~ "False"

Stage 7: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters, AdminAuditLogEnabledValue

Stage 8: extend (4 consecutive steps)

| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)
| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')
| extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName)
| extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '')
AccountName =
ifUserId contains "@"tostring(split(UserId, '@')[0])
elseUserId

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.

FieldKindValues
AdminAuditLogEnabledValueeq
  • False
OfficeWorkloadeq
  • Exchange
Operationeq
  • Set-AdminAuditLogConfig
UserTypein
  • Admin
  • DcAdmin

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.

FieldSource
AdminAuditLogEnabledValuesummarize
ClientIPsummarize
EndTimeUtcsummarize
Operationsummarize
OperationCountsummarize
Parameterssummarize
ResultStatussummarize
StartTimeUtcsummarize
UserIdsummarize
UserTypesummarize
AccountNameextend
AccountUPNSuffixextend
AccountNTDomainextend