Detection rules › Kusto
Rare Process as a Service
This query is looking for rarely seen processes which are launched as a service. Whiltelisted process list need to be updated based on the environment. Author: Jouni Mikkola More info: https://threathunt.blog/rare-process-launch-as-a-service/
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | T1543.003 Create or Modify System Process: Windows Service |
Event coverage
| Provider | Event/ActionType | Title |
|---|---|---|
| Sysmon | Event ID 1 | Process creation |
| Security-Auditing | Event ID 4688 | A new process has been created. |
| Defender-DeviceProcessEvents | any | Process activity (any) |
Rule body kusto
id: 91a451e3-178f-41b2-9e5d-da97d75b9971
name: Rare Process as a Service
description: |
This query is looking for rarely seen processes which are launched as a service. Whiltelisted process list need to be updated based on the environment.
Author: Jouni Mikkola
More info: https://threathunt.blog/rare-process-launch-as-a-service/
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
- DeviceNetworkEvents
- DeviceFileEvents
- DeviceImageLoadEvents
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1543
- T1543.003
query: |
let LookupTime = 14d;
let WhiteList = pack_array(
"svchost.exe",
"mssense.exe",
"msmpeng.exe",
"searchindexer.exe",
"microsoftedgeupdate.exe"
);
let GetServices = materialize (
DeviceProcessEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, StartedChildProcess = FileName, StartedChildProcessSHA1 = SHA1, StartedChildProcessCmdline = ProcessCommandLine, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName
);
GetServices
| summarize count() by ServiceProcess, DeviceName
| where count_ < 6
| join kind = inner GetServices on ServiceProcess, DeviceName
| join kind = leftouter (
DeviceNetworkEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName, NetworkAction = ActionType, RemoteIP, RemoteUrl
) on DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
| join kind = leftouter (
DeviceFileEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName, FileAction = ActionType, ModifiedFile = FileName, ModifiedFileSHA1 = SHA1, ModifiedFilePath = FolderPath
) on DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
| join kind = leftouter (
DeviceImageLoadEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName, LoadedDLL = FileName, LoadedDLLSHA1 = SHA1, LoadedDLLPath = FolderPath
) on DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
| summarize ConnectedAddresses = make_set(RemoteIP, 100000), ConnectedUrls = make_set(RemoteUrl, 100000), FilesModified = make_set(ModifiedFile, 100000),FileModFolderPath = make_set(ModifiedFilePath, 100000),FileModHA1s = make_set(ModifiedFileSHA1, 100000), ChildProcesses = make_set(StartedChildProcess, 100000), ChildCommandlines = make_set(StartedChildProcessCmdline, 100000), DLLsLoaded = make_set(LoadedDLL, 100000), DLLSHA1 = make_set(LoadedDLLSHA1, 100000) by DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: Process
fieldMappings:
- identifier: ProcessId
columnName: ServiceProcessID
- identifier: CommandLine
columnName: ServiceProcessCmdline
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let LookupTime = 14d;
Let binding: WhiteList
let WhiteList = pack_array(
"svchost.exe",
"mssense.exe",
"msmpeng.exe",
"searchindexer.exe",
"microsoftedgeupdate.exe"
);
The stages below define let GetServices (the rule's main pipeline source).
Stage 1: source
DeviceProcessEvents
Stage 2: where
| where TimeGenerated > ago(LookupTime)
Stage 3: where
| where InitiatingProcessParentFileName contains "services.exe"
Stage 4: where
| where InitiatingProcessFileName !in~(WhiteList)
References WhiteList (defined above).
Stage 5: project
| project TimeGenerated, DeviceName, StartedChildProcess = FileName, StartedChildProcessSHA1 = SHA1, StartedChildProcessCmdline = ProcessCommandLine, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName
The stages below run on GetServices (the outer pipeline).
Stage 6: summarize
GetServices
| summarize count() by ServiceProcess, DeviceName
Stage 7: where
| where count_ < 6
Stage 8: join
| join kind = inner GetServices on ServiceProcess, DeviceName
Stage 9: join
| join kind = leftouter (
DeviceNetworkEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName, NetworkAction = ActionType, RemoteIP, RemoteUrl
) on DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
Stage 10: join
| join kind = leftouter (
DeviceFileEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName, FileAction = ActionType, ModifiedFile = FileName, ModifiedFileSHA1 = SHA1, ModifiedFilePath = FolderPath
) on DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
Stage 11: join
| join kind = leftouter (
DeviceImageLoadEvents
| where TimeGenerated > ago(LookupTime)
| where InitiatingProcessParentFileName contains "services.exe"
| where InitiatingProcessFileName !in~(WhiteList)
| project TimeGenerated, DeviceName, ServiceProcessSHA1 = InitiatingProcessSHA1, ServiceProcess = InitiatingProcessFileName, ServiceProcessCmdline = InitiatingProcessCommandLine, ServiceProcessID = InitiatingProcessId, ServiceProcessCreationTime = InitiatingProcessCreationTime, ServiceProcessUser = InitiatingProcessAccountName, LoadedDLL = FileName, LoadedDLLSHA1 = SHA1, LoadedDLLPath = FolderPath
) on DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
Stage 12: summarize
| summarize ConnectedAddresses = make_set(RemoteIP, 100000), ConnectedUrls = make_set(RemoteUrl, 100000), FilesModified = make_set(ModifiedFile, 100000),FileModFolderPath = make_set(ModifiedFilePath, 100000),FileModHA1s = make_set(ModifiedFileSHA1, 100000), ChildProcesses = make_set(StartedChildProcess, 100000), ChildCommandlines = make_set(StartedChildProcessCmdline, 100000), DLLsLoaded = make_set(LoadedDLL, 100000), DLLSHA1 = make_set(LoadedDLLSHA1, 100000) by DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessUser, ServiceProcessSHA1
Stage 13: extend
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =DeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))DeviceNameStage 14: extend
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =DeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))""Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
InitiatingProcessFileName | eq | WhiteList |
InitiatingProcessFileName | eq | WhiteList |
InitiatingProcessFileName | eq | WhiteList |
InitiatingProcessFileName | eq | WhiteList |
InitiatingProcessFileName | eq | WhiteList |
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
InitiatingProcessParentFileName | contains |
|
count_ | lt |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
ChildCommandlines | summarize |
ChildProcesses | summarize |
ConnectedAddresses | summarize |
ConnectedUrls | summarize |
DLLSHA1 | summarize |
DLLsLoaded | summarize |
DeviceName | summarize |
FileModFolderPath | summarize |
FileModHA1s | summarize |
FilesModified | summarize |
ServiceProcess | summarize |
ServiceProcessCmdline | summarize |
ServiceProcessCreationTime | summarize |
ServiceProcessID | summarize |
ServiceProcessSHA1 | summarize |
ServiceProcessUser | summarize |
HostName | extend |
DnsDomain | extend |