Detection rules › Kusto

Audit policy manipulation using auditpol utility

Severity
medium
Time window
1d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

This detects attempts to manipulate audit policies using auditpol command. This technique was seen in relation to Solorigate attack but the results can indicate potential malicious activity used in different attacks. The process name in each data source is commented out as an adversary could rename it. It is advisable to keep process name commented but if the results show unrelated false positives, users may want to uncomment it. Refer to auditpol syntax: https://docs.microsoft.com/windows-server/administration/windows-commands/auditpol
Refer to our M365 blog for details on use during the Solorigate attack: https://www.microsoft.com/security/blog/2021/01/20/deep-dive-into-the-solorigate-second-stage-activation-from-sunburst-to-teardrop-and-raindrop/

MITRE ATT&CK coverage

TacticTechniques
ExecutionT1204 User Execution

Event coverage

ProviderEventTitle
SysmonEvent ID 1Process creation

Rule body kusto

id: 66276b14-32c5-4226-88e3-080dacc31ce1
name: Audit policy manipulation using auditpol utility
description: |
   This detects attempts to manipulate audit policies using auditpol command.
   This technique was seen in relation to Solorigate attack but the results can indicate potential malicious activity used in different attacks.
   The process name in each data source is commented out as an adversary could rename it. It is advisable to keep process name commented but if the results show unrelated false positives, users may want to uncomment it.
   Refer to auditpol syntax: https://docs.microsoft.com/windows-server/administration/windows-commands/auditpol  
   Refer to our M365 blog for details on use during the Solorigate attack:
   https://www.microsoft.com/security/blog/2021/01/20/deep-dive-into-the-solorigate-second-stage-activation-from-sunburst-to-teardrop-and-raindrop/
severity: Medium
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204
tags:
  - Solorigate
  - NOBELIUM
query: |
  let timeframe = 1d;
  let AccountAllowList = dynamic(['SYSTEM']);
  let SubCategoryList = dynamic(["Logoff", "Account Lockout", "User Account Management", "Authorization Policy Change"]); // Add any Category in the list to be allowed or disallowed
  let tokens = dynamic(["clear", "remove", "success:disable","failure:disable"]); 
  (union isfuzzy=true
  (
  SecurityEvent
  | where TimeGenerated >= ago(timeframe)
  //| where Process =~ "auditpol.exe" 
  | where CommandLine has_any (tokens)
  | where AccountType !~ "Machine" and Account !in~ (AccountAllowList)
  | parse CommandLine with * "/subcategory:" subcategorytoken
  | extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
  | where SubCategory in~ (SubCategoryList) //use in~ for inclusion or !in~ for exclusion
  | where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable") // use this filter if required to exclude certain toggles
  | project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, Process, ParentProcessName, CommandLine, SubCategory, Toggle
  | extend timestamp = TimeGenerated, AccountName = SubjectUserName, AccountDomain = SubjectDomainName, DeviceName = Computer
  ),
  (
  DeviceProcessEvents
  | where TimeGenerated >= ago(timeframe)
  // | where InitiatingProcessFileName =~ "auditpol.exe" 
  | where InitiatingProcessCommandLine has_any (tokens)
  | where AccountName !in~ (AccountAllowList)
  | parse InitiatingProcessCommandLine with * "/subcategory:" subcategorytoken
  | extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
  | where SubCategory in~ (SubCategoryList) //use in~ for inclusion or !in~ for exclusion
  | where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable") // use this filter if required to exclude certain toggles
  | project TimeGenerated, DeviceName, AccountName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessParentFileName,  InitiatingProcessCommandLine, SubCategory, Toggle
  | extend timestamp = TimeGenerated, AccountName = InitiatingProcessAccountName, AccountDomain = InitiatingProcessAccountDomain
  ),
  (
  Event
  | where TimeGenerated > ago(timeframe)
  | where Source == "Microsoft-Windows-Sysmon"
  | where EventID == 1
  | extend EventData = parse_xml(EventData).DataItem.EventData.Data
  | mv-expand bagexpansion=array EventData
  | evaluate bag_unpack(EventData)
  | extend Key=tostring(['@Name']), Value=['#text']
  | evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
  // | where OriginalFileName =~ "auditpol.exe"
  | where CommandLine has_any (tokens)
  | where User !in~ (AccountAllowList)
  | parse CommandLine with * "/subcategory:" subcategorytoken
  | extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])
  | where SubCategory in~ (SubCategoryList) //use in~ for inclusion or !in~ for exclusion
  | where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable") // use this filter if required to exclude certain toggles
  | project TimeGenerated, Computer, User, Process, ParentImage, CommandLine, SubCategory, Toggle
  | extend timestamp = TimeGenerated, AccountName = tostring(split(User, @'\')[1]), AccountUPNSuffix = tostring(split(User, @'\')[0]), DeviceName = Computer
  )
  )
  | extend Account = strcat(AccountDomain, "\\", AccountName)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
version: 1.2.3
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Stage 0: let

let timeframe = 1d;
let AccountAllowList = dynamic(['SYSTEM']);
let SubCategoryList = dynamic(["Logoff", "Account Lockout", "User Account Management", "Authorization Policy Change"]);
let tokens = dynamic(["clear", "remove", "success:disable","failure:disable"]); 

Stage 1: union

union isfuzzy=true

Stage 2: source

SecurityEvent

Stage 3: where time_window=86400s

| where TimeGenerated >= ago(timeframe)

Stage 4: where

| where CommandLine has_any (tokens)

Stage 5: where

| where AccountType !~ "Machine" and Account !in~ (AccountAllowList)

Stage 6: parse

| parse CommandLine with * "/subcategory:" subcategorytoken

Stage 7: extend

| extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])

Stage 8: where

| where SubCategory in~ (SubCategoryList)

Stage 9: where

| where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable")

Stage 10: project

| project TimeGenerated, Computer, Account, SubjectDomainName, SubjectUserName, Process, ParentProcessName, CommandLine, SubCategory, Toggle

Stage 11: extend

| extend timestamp = TimeGenerated, AccountName = SubjectUserName, AccountDomain = SubjectDomainName, DeviceName = Computer

Stage 12: source

DeviceProcessEvents

Stage 13: where time_window=86400s

| where TimeGenerated >= ago(timeframe)

Stage 14: where

| where InitiatingProcessCommandLine has_any (tokens)

Stage 15: where

| where AccountName !in~ (AccountAllowList)

Stage 16: parse

| parse InitiatingProcessCommandLine with * "/subcategory:" subcategorytoken

Stage 17: extend

| extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])

Stage 18: where

| where SubCategory in~ (SubCategoryList)

Stage 19: where

| where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable")

Stage 20: project

| project TimeGenerated, DeviceName, AccountName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessParentFileName,  InitiatingProcessCommandLine, SubCategory, Toggle

Stage 21: extend

| extend timestamp = TimeGenerated, AccountName = InitiatingProcessAccountName, AccountDomain = InitiatingProcessAccountDomain

Stage 22: source

Event

Stage 23: where time_window=86400s

| where TimeGenerated > ago(timeframe)

Stage 24: where

| where Source == "Microsoft-Windows-Sysmon"

Stage 25: where

| where EventID == 1

Stage 26: extend

| extend EventData = parse_xml(EventData).DataItem.EventData.Data

Stage 27: mv-expand

| mv-expand bagexpansion=array EventData

Stage 28: evaluate

| evaluate bag_unpack(EventData)

Stage 29: extend

| extend Key=tostring(['@Name']), Value=['#text']

Stage 30: evaluate

| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)

Stage 31: where

| where CommandLine has_any (tokens)

Stage 32: where

| where User !in~ (AccountAllowList)

Stage 33: parse

| parse CommandLine with * "/subcategory:" subcategorytoken

Stage 34: extend

| extend SubCategory = tostring(split(subcategorytoken, "\"")[1]) , Toggle =  tostring(split(subcategorytoken, "\"")[2])

Stage 35: where

| where SubCategory in~ (SubCategoryList)

Stage 36: where

| where Toggle !in~ ("/failure:disable", " /success:enable /failure:disable")

Stage 37: project

| project TimeGenerated, Computer, User, Process, ParentImage, CommandLine, SubCategory, Toggle

Stage 38: extend

| extend timestamp = TimeGenerated, AccountName = tostring(split(User, @'\')[1]), AccountUPNSuffix = tostring(split(User, @'\')[0]), DeviceName = Computer

Stage 39: extend

| extend Account = strcat(AccountDomain, "\\", AccountName)

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

StageFieldKindExcluded values
5AccounteqAccountAllowList
9Togglein /success:enable /failure:disable, /failure:disable
15AccountNameeqAccountAllowList
19Togglein /success:enable /failure:disable, /failure:disable
32UsereqAccountAllowList
36Togglein /success:enable /failure:disable, /failure:disable

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
AccountTypene
  • Machine corpus 2 (kusto 2)
CommandLinematch
  • clear corpus 4 (sigma 3, kusto 1)
  • failure:disable
  • remove corpus 2 (sigma 1, kusto 1)
  • success:disable
EventIDeq
  • 1 transforms: cased corpus 232 (splunk 223, kusto 9)
InitiatingProcessCommandLinematch
  • clear corpus 4 (sigma 3, kusto 1)
  • failure:disable
  • remove corpus 2 (sigma 1, kusto 1)
  • success:disable
SubCategoryin
  • Account Lockout
  • Authorization Policy Change
  • Logoff
  • User Account Management

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
CommandLineproject
Computerproject
ParentImageproject
Processproject
SubCategoryproject
TimeGeneratedproject
Toggleproject
Userproject
AccountNameextend
AccountUPNSuffixextend
DeviceNameextend
timestampextend
Accountextend