Detection rules › Kusto
COM Event System Loading New DLL
This query uses Sysmon Image Load (Event ID 7) and Process Create (Event ID 1) data to look for COM Event System being used to load a newly seen DLL.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 1: Process creation |
| Sysmon | Event ID 7: Image loaded |
Rule body
id: 02f6c2e5-219d-4426-a0bf-ad67abc63d53
name: COM Event System Loading New DLL
description: |
'This query uses Sysmon Image Load (Event ID 7) and Process Create (Event ID 1) data to look for COM Event System being used to load a newly seen DLL.'
severity: Medium
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1543
query: |
let lookback_start = 7d;
let lookback_end = 1d;
let timedelta = 5s;
// Get a list of previously seen DLLs being loaded
let known_dlls = (Event
| where TimeGenerated between(ago(lookback_start)..ago(lookback_end))
| where EventID == 7
| extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend LoadedItems = parse_json(tostring(parse_json(tostring(EvData.DataItem)).EventData)).["Data"]
| mv-expand LoadedItems
| where tostring(LoadedItems.["@Name"]) =~ "ImageLoaded"
| extend DLL = tostring(LoadedItems.["#text"])
| summarize by DLL);
// Get Image Load events related to svchost.exe
Event
| where Source =~ "Microsoft-Windows-Sysmon"
// Image Load Event in Sysmon
| where EventID == 7
| extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend Images = parse_json(tostring(parse_json(tostring(EvData.DataItem)).EventData)).["Data"]
| mv-expand Images
// Parse out executing process
| where tostring(Images.["@Name"]) =~ "Image"
| extend Image = tostring(Images.["#text"])
| where Image endswith "\\svchost.exe"
// Parse out loaded DLLs
| extend LoadedItems = parse_json(tostring(parse_json(tostring(EvData.DataItem)).EventData)).["Data"]
| mv-expand LoadedItems
| where tostring(LoadedItems.["@Name"]) =~ "ImageLoaded"
| extend DLL = tostring(LoadedItems.["#text"])
| extend Image = tostring(Image)
| extend ImageLoadTime = TimeGenerated
// Join with processes with a command line related to COM Event System
| join kind = inner(Event
| where Source =~ "Microsoft-Windows-Sysmon"
// Sysmon process execution events
| where EventID == 1
| extend RenderedDescription = tostring(split(RenderedDescription, ":")[0])
| extend EventData = parse_xml(EventData).DataItem.EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key = tostring(column_ifexists('@Name', "")), Value = column_ifexists('#text', "")
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
| extend ParentImage = tostring(column_ifexists("ParentImage", "NotAvailable"))
// Command line related to COM Event System
| where ParentImage endswith "\\svchost.exe"
//| where ParentCommandLine has_all (" -k LocalService"," -p"," -s EventSystem")
| extend ProcessExecutionTime = TimeGenerated) on $left.Image == $right.ParentImage
// Check timespan between DLL load and process creation
| extend delta = ProcessExecutionTime - ImageLoadTime
| where ImageLoadTime <= ProcessExecutionTime and delta <= timedelta
// Filter to only newly seen DLLs
| where DLL !in (known_dlls)
| extend ParentCommandLine = tostring(column_ifexists("ParentCommandLine", "NotAvailable"))
| project-reorder ImageLoadTime, ProcessExecutionTime , Image, ParentCommandLine, DLL
| extend Hashes = tostring(column_ifexists("Hashes", "NotAvailable, NotAvailable"))
| extend Hashes = split(Hashes, ",")
| mv-apply Hashes on (summarize FileHashes = make_bag(pack(tostring(split(Hashes, "=")[0]), tostring(split(Hashes, "=")[1]))))
| extend SHA1 = tostring(FileHashes.SHA1)
| extend HashAlgo = "SHA1"
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend Name = tostring(split(UserName, "\\")[1]), NTDomain = tostring(split(UserName, "\\")[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserName
- identifier: Name
columnName: Name
- identifier: NTDomain
columnName: NTDomain
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: FileHash
fieldMappings:
- identifier: Value
columnName: SHA1
- identifier: Algorithm
columnName: HashAlgo
version: 1.0.4
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Shain
support:
tier: Community
categories:
domains: [ "Security - Others" ]
Stages and Predicates
Parameters
let lookback_start = 7d;
let lookback_end = 1d;
let timedelta = 5s;
Let binding: known_dlls
let known_dlls = (Event
| where TimeGenerated between(ago(lookback_start)..ago(lookback_end))
| where EventID == 7
| extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend LoadedItems = parse_json(tostring(parse_json(tostring(EvData.DataItem)).EventData)).["Data"]
| mv-expand LoadedItems
| where tostring(LoadedItems.["@Name"]) =~ "ImageLoaded"
| extend DLL = tostring(LoadedItems.["#text"])
| summarize by DLL);
Stage 1: source
Event
Stage 2: where
where Source =~ "Microsoft-Windows-Sysmon"
Stage 3: where
where EventID == 7
Stage 4: extend (3 consecutive steps)
extend EvData, EventDetail, Images
Stage 5: mv-expand
mv-expand Images
Stage 6: where
where @Name =~ "Image"
Stage 7: extend
extend Image
Stage 8: where
where Image endswith @"\svchost.exe"
Stage 9: extend
extend LoadedItems
Stage 10: mv-expand
mv-expand LoadedItems
Stage 11: where
where @Name =~ "ImageLoaded"
Stage 12: extend (3 consecutive steps)
extend DLL, Image, ImageLoadTime
Stage 13: join
join kind=inner (Event) on Image, ParentImage
Stage 14: extend
extend delta
Stage 15: where
where ImageLoadTime <= ProcessExecutionTime and delta <= "5s"
Stage 16: where
where not (DLL =~ "known_dlls")
Stage 17: extend
extend ParentCommandLine
Stage 18: project-reorder
project-reorder
Stage 19: extend
extend Hashes
Stage 20: extend
extend Hashes
Stage 21: kusto:mv-apply
kusto:mv-apply
Stage 22: extend (5 consecutive steps)
extend DomainIndex, HashAlgo, HostName, HostNameDomain, NTDomain, Name, SHA1
Stage 23: summarize aggregation inside the mv-apply branch
summarize
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DLL | eq | known_dlls | excludes:DLL field:"DLL" value:"known_dlls" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
@Name | eq |
| field:"@Name" kind:eq |
EventID | eq |
| field:"EventID" kind:eq |
Image | ends_with |
| field:"Image" kind:ends_with value:"\svchost.exe" |
ImageLoadTime | cross_field_compare |
| field:"ImageLoadTime" kind:cross_field_compare value:"ProcessExecutionTime" |
ParentImage | ends_with |
| field:"ParentImage" kind:ends_with value:"\svchost.exe" |
delta | le |
| field:"delta" kind:le value:"5s" |