Detection rules › Kusto
PowerShell without powershell.exe
This query detects the use of PowerShell through "system.management.automation.dll" which is invoked by a process with a low global prevalence (i.e., fairly unique binary).
Known false positives
- Many legitimate programs will use the "system.management.automation.dll". This might lead to false-positives that require filtering.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution |
Telemetry coverage
Rule body
let timeframe = 2*1h;
let default_global_prevalence = 0;
let suspiciousProcs = materialize(
DeviceImageLoadEvents
| where FileName =~ "system.management.automation.dll" or FileName =~ "system.management.automation.ni.dll" and not(isempty(InitiatingProcessSHA1))
| summarize count() by InitiatingProcessSHA1
// FileProfile is case-sensitive and works on lower-case hashes.
| extend InitiatingProcessSHA1=tolower(InitiatingProcessSHA1)
| invoke FileProfile(InitiatingProcessSHA1, 1000)
| where not(ProfileAvailability =~ "Error")
| where coalesce(IsRootSignerMicrosoft, false) == false or coalesce(IsCertificateValid, false) == false
| where (IsCertificateValid and coalesce(GlobalPrevalence,default_global_prevalence) < 200) or (not(coalesce(IsCertificateValid, false)) and coalesce(GlobalPrevalence,default_global_prevalence) < 500));
DeviceImageLoadEvents
| where ingestion_time() >= ago(timeframe)
// FileProfile is case-sensitive and works on lower-case hashes.
| extend InitiatingProcessSHA1=tolower(InitiatingProcessSHA1)
| where InitiatingProcessSHA1 in~ ((suspiciousProcs | project InitiatingProcessSHA1)) and FileName startswith "System.Management.Automation"
| join kind=inner suspiciousProcs on InitiatingProcessSHA1
// Begin environment-specific filter.
// End environment-specific filter.
Stages and Predicates
Parameters
let timeframe = 2*1h;
let default_global_prevalence = 0;
Let binding: suspiciousProcs
let suspiciousProcs = materialize(
DeviceImageLoadEvents
| where FileName =~ "system.management.automation.dll" or FileName =~ "system.management.automation.ni.dll" and not(isempty(InitiatingProcessSHA1))
| summarize count() by InitiatingProcessSHA1
| extend InitiatingProcessSHA1=tolower(InitiatingProcessSHA1)
| invoke FileProfile(InitiatingProcessSHA1, 1000)
| where not(ProfileAvailability =~ "Error")
| where coalesce(IsRootSignerMicrosoft, false) == false or coalesce(IsCertificateValid, false) == false
| where (IsCertificateValid and coalesce(GlobalPrevalence,default_global_prevalence) < 200) or (not(coalesce(IsCertificateValid, false)) and coalesce(GlobalPrevalence,default_global_prevalence) < 500));
Stage 1: source
let suspiciousProcs
Stage 2: source
DeviceImageLoadEvents
Stage 3: where
where ...
Stage 4: extend
extend InitiatingProcessSHA1
Stage 5: where
where FileName startswith "System.Management.Automation"
Stage 6: join
join kind=inner (suspiciousProcs) on InitiatingProcessSHA1
Stage 7: summarize aggregation inside the join branch
summarize by InitiatingProcessSHA1
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ProfileAvailability | eq | Error | excludes:ProfileAvailability field:"ProfileAvailability" value:"Error" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FileName | eq |
| field:"file_name" kind:eq |
FileName | starts_with |
| field:"file_name" kind:starts_with value:"System.Management.Automation" |
GlobalPrevalence | lt |
| field:"GlobalPrevalence" kind:lt |
IsCertificateValid | eq |
| field:"IsCertificateValid" kind:eq value:"false" |
IsRootSignerMicrosoft | eq |
| field:"IsRootSignerMicrosoft" kind:eq value:"false" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
InitiatingProcessSHA1 | summarize |