Detection rules › Kusto
Powershell Empire Cmdlets Executed in Command Line
This query identifies use of PowerShell Empire's cmdlets within the command line data of the PowerShell process, indicating potential use of the post-exploitation tool.
MITRE ATT&CK coverage
Event coverage
| Provider | Event | Title |
|---|---|---|
| Security-Auditing | Event ID 4688 | A new process has been created. |
Rule body kusto
id: ef88eb96-861c-43a0-ab16-f3835a97c928
name: Powershell Empire Cmdlets Executed in Command Line
description: |
'This query identifies use of PowerShell Empire's cmdlets within the command line data of the PowerShell process, indicating potential use of the post-exploitation tool.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvents
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 12h
queryPeriod: 12h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
- CommandAndControl
- CredentialAccess
- DefenseEvasion
- Discovery
- Execution
- Exfiltration
- LateralMovement
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1548.002
- T1134
- T1134.002
- T1134.005
- T1087.001
- T1087.002
- T1557.001
- T1071.001
- T1560
- T1547.001
- T1547.005
- T1547.009
- T1217
- T1115
- T1059
- T1059.001
- T1059.003
- T1136.001
- T1136.002
- T1543.003
- T1555.003
- T1484.001
- T1482
- T1114.001
- T1573.002
- T1546.008
- T1041
- T1567.001
- T1567.002
- T1068
- T1210
- T1083
- T1615
- T1574.001
- T1574.004
- T1574.007
- T1574.008
- T1574.009
- T1070.006
- T1105
- T1056.001
- T1056.004
- T1106
- T1046
- T1135
- T1040
- T1027
- T1003.001
- T1057
- T1055
- T1021.003
- T1021.004
- T1053.005
- T1113
- T1518.001
- T1558.002
- T1558.003
- T1082
- T1016
- T1049
- T1569.002
- T1127.001
- T1552.001
- T1552.004
- T1550.002
- T1125
- T1102.002
- T1047
query: |
let regexEmpire = tostring(toscalar(externaldata(cmdlets:string)[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/EmpireCommandString.txt"] with (format="txt")));
(union isfuzzy=true
(SecurityEvent
| where EventID == 4688
//consider filtering on filename if perf issues occur
//where FileName in~ ("powershell.exe","powershell_ise.exe","pwsh.exe")
| where not(ParentProcessName has_any ('gc_worker.exe', 'gc_service.exe'))
| where CommandLine has "-encodedCommand"
| parse kind=regex flags=i CommandLine with * "-EncodedCommand " encodedCommand
| extend encodedCommand = iff(encodedCommand has " ", tostring(split(encodedCommand, " ")[0]), encodedCommand)
// Note: currently the base64_decode_tostring function is limited to supporting UTF8
| extend decodedCommand = translate('\0','', base64_decode_tostring(substring(encodedCommand, 0, strlen(encodedCommand) - (strlen(encodedCommand) %8)))), encodedCommand, CommandLine , strlen(encodedCommand)
| extend EfectiveCommand = iff(isnotempty(encodedCommand), decodedCommand, CommandLine)
| where EfectiveCommand matches regex regexEmpire
| project timestamp = TimeGenerated, Computer, SubjectUserName, SubjectDomainName, FileName = Process, EfectiveCommand, decodedCommand, encodedCommand, CommandLine, ParentProcessName
| extend HostName = split(Computer, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(Computer, '.'), 1, -1), '.')
),
(WindowsEvent
| where EventID == 4688
| where EventData has_any ("-encodedCommand", "powershell.exe","powershell_ise.exe","pwsh.exe")
| where not(EventData has_any ('gc_worker.exe', 'gc_service.exe'))
//consider filtering on filename if perf issues occur
//extend NewProcessName = tostring(EventData.NewProcessName)
//extend Process=tostring(split(NewProcessName, '\\')[-1])
//FileName = Process
//where FileName in~ ("powershell.exe","powershell_ise.exe","pwsh.exe")
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| where not(ParentProcessName has_any ('gc_worker.exe', 'gc_service.exe'))
| extend CommandLine = tostring(EventData.CommandLine)
| where CommandLine has "-encodedCommand"
| parse kind=regex flags=i CommandLine with * "-EncodedCommand " encodedCommand
| extend encodedCommand = iff(encodedCommand has " ", tostring(split(encodedCommand, " ")[0]), encodedCommand)
// Note: currently the base64_decode_tostring function is limited to supporting UTF8
| extend decodedCommand = translate('\0','', base64_decode_tostring(substring(encodedCommand, 0, strlen(encodedCommand) - (strlen(encodedCommand) %8)))), encodedCommand, CommandLine , strlen(encodedCommand)
| extend EfectiveCommand = iff(isnotempty(encodedCommand), decodedCommand, CommandLine)
| where EfectiveCommand matches regex regexEmpire
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
| project timestamp = TimeGenerated, Computer, SubjectUserName, SubjectDomainName, FileName = Process, EfectiveCommand, decodedCommand, encodedCommand, CommandLine, ParentProcessName
| extend HostName = split(Computer, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(Computer, '.'), 1, -1), '.')
))
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: SubjectUserName
- identifier: NTDomain
columnName: SubjectDomainName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.3.1
kind: Scheduled
Stages and Predicates
Stage 0: let
let regexEmpire = tostring(toscalar(externaldata(cmdlets:string)[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/EmpireCommandString.txt"] with (format="txt")));
Stage 1: union
union isfuzzy=true
Stage 2: source time_window=43200s
SecurityEvent
Stage 3: where
| where EventID == 4688
Stage 4: where
| where not(ParentProcessName has_any ('gc_worker.exe', 'gc_service.exe'))
Stage 5: where
| where CommandLine has "-encodedCommand"
Stage 6: parse
| parse kind=regex flags=i CommandLine with * "-EncodedCommand " encodedCommand
Stage 7: extend
| extend encodedCommand = iff(encodedCommand has " ", tostring(split(encodedCommand, " ")[0]), encodedCommand)
Stage 8: extend
| extend decodedCommand = translate('\0','', base64_decode_tostring(substring(encodedCommand, 0, strlen(encodedCommand) - (strlen(encodedCommand) %8)))), encodedCommand, CommandLine , strlen(encodedCommand)
Stage 9: extend
| extend EfectiveCommand = iff(isnotempty(encodedCommand), decodedCommand, CommandLine)
Stage 10: where
| where EfectiveCommand matches regex regexEmpire
Stage 11: project
| project timestamp = TimeGenerated, Computer, SubjectUserName, SubjectDomainName, FileName = Process, EfectiveCommand, decodedCommand, encodedCommand, CommandLine, ParentProcessName
Stage 12: extend
| extend HostName = split(Computer, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(Computer, '.'), 1, -1), '.')
Stage 13: source
WindowsEvent
Stage 14: where
| where EventID == 4688
Stage 15: where
| where EventData has_any ("-encodedCommand", "powershell.exe","powershell_ise.exe","pwsh.exe")
Stage 16: where
| where not(EventData has_any ('gc_worker.exe', 'gc_service.exe'))
Stage 17: extend
| extend ParentProcessName = tostring(EventData.ParentProcessName)
Stage 18: where
| where not(ParentProcessName has_any ('gc_worker.exe', 'gc_service.exe'))
Stage 19: extend
| extend CommandLine = tostring(EventData.CommandLine)
Stage 20: where
| where CommandLine has "-encodedCommand"
Stage 21: parse
| parse kind=regex flags=i CommandLine with * "-EncodedCommand " encodedCommand
Stage 22: extend
| extend encodedCommand = iff(encodedCommand has " ", tostring(split(encodedCommand, " ")[0]), encodedCommand)
Stage 23: extend
| extend decodedCommand = translate('\0','', base64_decode_tostring(substring(encodedCommand, 0, strlen(encodedCommand) - (strlen(encodedCommand) %8)))), encodedCommand, CommandLine , strlen(encodedCommand)
Stage 24: extend
| extend EfectiveCommand = iff(isnotempty(encodedCommand), decodedCommand, CommandLine)
Stage 25: where
| where EfectiveCommand matches regex regexEmpire
Stage 26: extend
| extend SubjectUserName = tostring(EventData.SubjectUserName)
Stage 27: extend
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
Stage 28: extend
| extend NewProcessName = tostring(EventData.NewProcessName)
Stage 29: extend
| extend Process=tostring(split(NewProcessName, '\\')[-1])
Stage 30: project
| project timestamp = TimeGenerated, Computer, SubjectUserName, SubjectDomainName, FileName = Process, EfectiveCommand, decodedCommand, encodedCommand, CommandLine, ParentProcessName
Stage 31: extend
| extend HostName = split(Computer, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(Computer, '.'), 1, -1), '.')
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Stage | Field | Kind | Excluded values |
|---|---|---|---|
| 4 | ParentProcessName | match | gc_worker.exe, gc_service.exe |
| 16 | EventData | match | gc_worker.exe, gc_service.exe |
| 18 | ParentProcessName | match | gc_worker.exe, gc_service.exe |
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 |
|---|---|---|
CommandLine | match |
|
EfectiveCommand | regex_match |
|
EventData | match |
|
EventID | 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 |
|---|---|
CommandLine | project |
Computer | project |
EfectiveCommand | project |
FileName | project |
ParentProcessName | project |
SubjectDomainName | project |
SubjectUserName | project |
decodedCommand | project |
encodedCommand | project |
timestamp | project |
DnsDomain | extend |
HostName | extend |