Detection rules › Kusto
Script Interpreter Loading DotNet Assembly From Memory
The query searches for script interpreters (mmc.exe, mshta.exe, wscript.exe, and cscript.exe) loading .NET assemblies from memory. In the case of the MMC executable, the query also checks for the MSC file that was loaded, as some legitimate MSC files are known to load .NET assemblies via MMC.
Known false positives
- Some custom MMC snap-ins may load .NET assemblies, which can trigger this alert.
MITRE ATT&CK coverage
References
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceEvents action ClrUnbackedModuleLoaded: CLR unbacked module loaded |
Rule body
let timeframe = 2*1h;
DeviceEvents
| where ingestion_time() >= ago(timeframe)
| where ActionType == "ClrUnbackedModuleLoaded"
| where InitiatingProcessFileName in~ ("mmc.exe","mshta.exe","wscript.exe","cscript.exe")
| extend DotNetAssemblyName = tostring(parse_json(AdditionalFields).ModuleILPathOrName)
| extend ParsedCommandLine=parse_command_line(InitiatingProcessCommandLine, "windows")
// When a .msc file is opened in MMC, the file path is passed as an argument to MMC.
// Based on testing this is the first argument in the command line. In some cases a command-line switch /32 is passed as the first argument
// and the file path is the second argument. This is handled by the iif statement below.
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)
// Some Microsoft MSC files are known to load .NET assemblies via MMC.
| where not(InitiatingProcessFileName =~ "mmc.exe" and (MscFile startswith @"c:\windows\system32\" or MscFile startswith @"C:\Program Files\Update Services\" or MscFile startswith @"C:\ProgramData\Microsoft\Windows\Start Menu\"))
// Do not alert when MMC is started without any arguments, since in that case it is unknown which MSC file was loaded.
| where not(InitiatingProcessFileName =~ "mmc.exe" and (array_length(ParsedCommandLine) == 1))
// Begin environment-specific filter.
// End environment-specific filter.
Stages and Predicates
Parameters
let timeframe = 2*1h;
Stage 1: source
DeviceEvents
Stage 2: where
| where ingestion_time() >= ago(timeframe)
Stage 3: where
| where ActionType == "ClrUnbackedModuleLoaded"
Stage 4: where
| where InitiatingProcessFileName in~ ("mmc.exe","mshta.exe","wscript.exe","cscript.exe")
Stage 5: extend (4 consecutive steps)
| extend DotNetAssemblyName = tostring(parse_json(AdditionalFields).ModuleILPathOrName)
| extend ParsedCommandLine=parse_command_line(InitiatingProcessCommandLine, "windows")
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)
Stage 6: where
| where not(InitiatingProcessFileName =~ "mmc.exe" and (MscFile startswith @"c:\windows\system32\" or MscFile startswith @"C:\Program Files\Update Services\" or MscFile startswith @"C:\ProgramData\Microsoft\Windows\Start Menu\"))
Stage 7: where
| where not(InitiatingProcessFileName =~ "mmc.exe" and (array_length(ParsedCommandLine) == 1))
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
MscFile | starts_with | C:\Program Files\Update Services\ | excludes:MscFile field:"MscFile" value:"C:\Program Files\Update Services\" |
MscFile | starts_with | C:\ProgramData\Microsoft\Windows\Start Menu\ | excludes:MscFile field:"MscFile" value:"C:\ProgramData\Microsoft\Windows\Start Menu\" |
MscFile | starts_with | c:\windows\system32\ | excludes:MscFile field:"MscFile" value:"c:\windows\system32\" |
InitiatingProcessFileName | eq | mmc.exe | excludes:InitiatingProcessFileName field:"InitiatingProcessFileName" value:"mmc.exe" |
ParsedCommandLine | eq | 1 | excludes:ParsedCommandLine field:"ParsedCommandLine" value:"1" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"ClrUnbackedModuleLoaded" |
InitiatingProcessFileName | in |
| field:"parent_process_name" kind:in |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DotNetAssemblyName | extend |
ParsedCommandLine | extend |
MscFile | extend |