Detection rules › Kusto

Rare Process as a Service

Status
available
Severity
medium
Time window
14d
Group by
DeviceName, ServiceProcess, ServiceProcessCmdline, ServiceProcessCreationTime, ServiceProcessID, ServiceProcessSHA1, ServiceProcessUser
Source
github.com/Azure/Azure-Sentinel

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

Event coverage

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 =
ifDeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))
elseDeviceName

Stage 14: extend

| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =
ifDeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))
else""

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
InitiatingProcessFileNameeqWhiteList
InitiatingProcessFileNameeqWhiteList
InitiatingProcessFileNameeqWhiteList
InitiatingProcessFileNameeqWhiteList
InitiatingProcessFileNameeqWhiteList

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.

FieldKindValues
InitiatingProcessParentFileNamecontains
  • services.exe
count_lt
  • 6 transforms: cased

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.

FieldSource
ChildCommandlinessummarize
ChildProcessessummarize
ConnectedAddressessummarize
ConnectedUrlssummarize
DLLSHA1summarize
DLLsLoadedsummarize
DeviceNamesummarize
FileModFolderPathsummarize
FileModHA1ssummarize
FilesModifiedsummarize
ServiceProcesssummarize
ServiceProcessCmdlinesummarize
ServiceProcessCreationTimesummarize
ServiceProcessIDsummarize
ServiceProcessSHA1summarize
ServiceProcessUsersummarize
HostNameextend
DnsDomainextend