Detection rules › Kusto
Audit policy manipulation using auditpol utility
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
| Tactic | Techniques |
|---|---|
| Execution | T1204 User Execution |
Event coverage
| Provider | Event | Title |
|---|---|---|
| Sysmon | Event ID 1 | Process 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.
| Stage | Field | Kind | Excluded values |
|---|---|---|---|
| 5 | Account | eq | AccountAllowList |
| 9 | Toggle | in | /success:enable /failure:disable, /failure:disable |
| 15 | AccountName | eq | AccountAllowList |
| 19 | Toggle | in | /success:enable /failure:disable, /failure:disable |
| 32 | User | eq | AccountAllowList |
| 36 | Toggle | in | /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.
| Field | Kind | Values |
|---|---|---|
AccountType | ne |
|
CommandLine | match |
|
EventID | eq |
|
InitiatingProcessCommandLine | match |
|
SubCategory | in |
|
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 |
|---|---|
CommandLine | project |
Computer | project |
ParentImage | project |
Process | project |
SubCategory | project |
TimeGenerated | project |
Toggle | project |
User | project |
AccountName | extend |
AccountUPNSuffix | extend |
DeviceName | extend |
timestamp | extend |
Account | extend |