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
Execution

Telemetry coverage

Rule body

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 of 3 branches

Stage 2: source

SecurityEvent

Stage 3: where

| 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

| 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

| 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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AccountTypene
  • Machine
field:"AccountType" kind:ne value:"Machine"
CommandLinematch
  • clear transforms: term corpus 4 (sigma 3, kusto 1)
  • failure:disable transforms: term
  • remove transforms: term corpus 4 (sigma 3, kusto 1)
  • success:disable transforms: term
field:"CommandLine" kind:match
EventIDeq
  • 1 corpus 242 (splunk 226, kusto 15, elastic 1)
field:"EventID" kind:eq value:"1"
InitiatingProcessCommandLinematch
  • clear transforms: term corpus 4 (sigma 3, kusto 1)
  • failure:disable transforms: term
  • remove transforms: term corpus 4 (sigma 3, kusto 1)
  • success:disable transforms: term
field:"CommandLine" kind:match
SubCategoryin
  • Account Lockout
  • Authorization Policy Change
  • Logoff
  • User Account Management
field:"SubCategory" kind:in

Exclusions

The rule actively suppresses these predicates.

Output fields

These fields are emitted when the rule matches.

FieldSource
CommandLineproject
Computerproject
ParentImageproject
Processproject
SubCategoryproject
TimeGeneratedproject
Toggleproject
Userproject
AccountNameextend
AccountUPNSuffixextend
DeviceNameextend
timestampextend
Accountextend