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
Telemetry coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule body
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 of 2 branches
Stage 2: source
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 (3 consecutive steps)
| extend encodedCommand = iff(encodedCommand has " ", tostring(split(encodedCommand, " ")[0]), encodedCommand)
| 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)
encodedCommand =if
encodedCommand has " "tostring(split(encodedCommand, " ")[0])else
encodedCommandStage 8: where
| where EfectiveCommand matches regex regexEmpire
Stage 9: project
| project timestamp = TimeGenerated, Computer, SubjectUserName, SubjectDomainName, FileName = Process, EfectiveCommand, decodedCommand, encodedCommand, CommandLine, ParentProcessName
Stage 10: extend
| extend HostName = split(Computer, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(Computer, '.'), 1, -1), '.')
Stage 11: source
WindowsEvent
Stage 12: where
| where EventID == 4688
Stage 13: where
| where EventData has_any ("-encodedCommand", "powershell.exe","powershell_ise.exe","pwsh.exe")
Stage 14: where
| where not(EventData has_any ('gc_worker.exe', 'gc_service.exe'))
Stage 15: extend
| extend ParentProcessName = tostring(EventData.ParentProcessName)
Stage 16: where
| where not(ParentProcessName has_any ('gc_worker.exe', 'gc_service.exe'))
Stage 17: extend
| extend CommandLine = tostring(EventData.CommandLine)
Stage 18: where
| where CommandLine has "-encodedCommand"
Stage 19: parse
| parse kind=regex flags=i CommandLine with * "-EncodedCommand " encodedCommand
Stage 20: extend (3 consecutive steps)
| extend encodedCommand = iff(encodedCommand has " ", tostring(split(encodedCommand, " ")[0]), encodedCommand)
| 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)
encodedCommand =if
encodedCommand has " "tostring(split(encodedCommand, " ")[0])else
encodedCommandStage 21: where
| where EfectiveCommand matches regex regexEmpire
Stage 22: extend (4 consecutive steps)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
Stage 23: project
| project timestamp = TimeGenerated, Computer, SubjectUserName, SubjectDomainName, FileName = Process, EfectiveCommand, decodedCommand, encodedCommand, CommandLine, ParentProcessName
Stage 24: extend
| extend HostName = split(Computer, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(Computer, '.'), 1, -1), '.')
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | match |
| field:"CommandLine" kind:match value:"-encodedCommand" |
EfectiveCommand | regex_match |
| field:"EfectiveCommand" kind:regex_match value:"regexEmpire" |
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ParentProcessName | match | gc_worker.exe, gc_service.exe | excludes:ParentProcessName field:"ParentProcessName" value:"gc_worker.exe" field:"ParentProcessName" value:"gc_service.exe" |
EventData | match | gc_worker.exe, gc_service.exe | excludes:EventData field:"EventData" value:"gc_worker.exe" field:"EventData" value:"gc_service.exe" |
Output fields
These fields are emitted when the rule matches.
| 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 |