Detection rules › Kusto
WinRM Plugin Lateral Movement
This query detects loading of malicious WinRM plugins. These plugins can be used for lateral movement. This tradecraft has been researched and published by Arnau Ortega at FalconForce. Refer to the references for the blog post describing the full attack chain. This detection looks at low-prevalence DLLs being loaded into the WinRM host process. To minimize false-positives, the detection looks for files that are written to disk in the last 30 days, prior to being loaded into the WinRM host process as DLL. Such DLLs are likely WinRM plugins that are being loaded. Since the use of WinRM plugins is extremely scarce in real environments, we assume that any such DLL is malicious and warrants an investigation.
Known false positives
- None expected.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Telemetry coverage
Rule body
let timeframe = 2*1d;
let default_global_prevalence = 0;
let lookback = 30d;
let PotentialPlugins = materialize(
DeviceImageLoadEvents
| where ingestion_time() >= ago(timeframe)
| where InitiatingProcessFileName =~ "wsmprovhost.exe"
| where FolderPath !startswith @"C:\windows\assembly\nativeimages_" // Excluding .NET GAC as these are irrelevant for WinRM plugins and generate false positives.
| invoke FileProfile(SHA1, 1000)
| where ProfileAvailability !~ "Error"
| where coalesce(GlobalPrevalence, default_global_prevalence) < 100
| extend FolderPath=tolower(FolderPath)
);
let PotentialWrites = (
DeviceFileEvents
| where Timestamp >= ago(lookback)
| where ActionType in~ ("FileCreated", "FileRenamed", "FileModified")
| where SHA1 in~ ((PotentialPlugins | project SHA1))
| extend FolderPath=tolower(FolderPath)
);
PotentialPlugins
| join kind=inner PotentialWrites on SHA1, DeviceId, FolderPath
// Begin environment-specific filter.
// End environment-specific filter.
Stages and Predicates
Parameters
let timeframe = 2*1d;
let default_global_prevalence = 0;
let lookback = 30d;
let PotentialPlugins is inlined into the numbered stages below.
Let binding: PotentialWrites
let PotentialWrites = (
DeviceFileEvents
| where Timestamp >= ago(lookback)
| where ActionType in~ ("FileCreated", "FileRenamed", "FileModified")
| where SHA1 in~ ((PotentialPlugins | project SHA1))
| extend FolderPath=tolower(FolderPath)
);
Stage 1: source
let PotentialPlugins
Stage 2: source
let PotentialWrites
Stage 3: source
DeviceImageLoadEvents
Stage 4: where
where ...
Stage 5: where
where InitiatingProcessFileName =~ "wsmprovhost.exe"
Stage 6: where
where not (FolderPath startswith @"C:\windows\assembly\nativeimages_")
Stage 7: invoke
invoke
Stage 8: where
where ProfileAvailability !~ "Error"
Stage 9: where
where GlobalPrevalence < 100
Stage 10: extend
extend FolderPath
Stage 11: join
join kind=inner (PotentialWrites) on SHA1, DeviceId, FolderPath
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
FolderPath | starts_with | C:\windows\assembly\nativeimages_ | excludes:FolderPath field:"FolderPath" value:"C:\windows\assembly\nativeimages_" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | in |
| field:"ActionType" kind:in |
GlobalPrevalence | lt |
| field:"GlobalPrevalence" kind:lt value:"100" |
InitiatingProcessFileName | eq |
| field:"parent_process_name" kind:eq value:"wsmprovhost.exe" |
ProfileAvailability | ne |
| field:"ProfileAvailability" kind:ne value:"Error" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
FolderPath | extend |