Detection rules › Kusto

WinRM Plugin Lateral Movement

Group by
DeviceId, FolderPath, SHA1
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

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

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 used in Stages 2, 11

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.

FieldKindExcluded valuesSearch
FolderPathstarts_withC:\windows\assembly\nativeimages_excludes:FolderPath field:"FolderPath" value:"C:\windows\assembly\nativeimages_"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
FolderPathextend