Detection rules › Kusto
Process Tree Analysis
Below queries perform process tree analysis on MDE/MDATP, Azure Sentinel (Sysmon), and Splunk (Sysmon) and displays anomalous trees. All queries run smoothly even in the large environments. Detailed explanation is here
References
Telemetry coverage
Rule body
let timeframe = 48h;
// Define of which processes you want to generate process tree
let _selected_processes = dynamic(["winword.exe","excel.exe","powerpnt.exe","acrord32.exe", "FoxitPhantomPDF.exe","MicrosoftPdfReader.exe","SumatraPDF.exe", "onenote.exe"]);
// First, generate the process tree and store it in the cache.
// Renaming fields accordingly to generate a tree up to 7th level
// In each step, project only the required fields to optimize resource usage
let _process_tree_data= materialize
( DeviceProcessEvents
| where Timestamp > ago(timeframe)
| where InitiatingProcessFileName in~ (_selected_processes )
| project DeviceId,DeviceName,
InitiatingProcessG3ParentFileName=FileName,InitiatingProcessG3ParentSHA1=SHA1,InitiatingProcessG3ParentId=ProcessId, InitiatingProcessG3ParentCommandLine=ProcessCommandLine,InitiatingProcessG3ParentCreationTime=todatetime(ProcessCreationTime),
InitiatingProcessG4ParentFileName=InitiatingProcessFileName,InitiatingProcessG4ParentSHA1=InitiatingProcessSHA1,InitiatingProcessG4ParentId=InitiatingProcessId,InitiatingProcessG4ParentCommandLine=InitiatingProcessCommandLine, InitiatingProcessG4ParentCreationTime=todatetime(InitiatingProcessCreationTime)
// Start iteration
// 1st iteration of join. From now on, query all processes, rename fields, and join accordingly
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(timeframe)
| project DeviceId, InitiatingProcessG2ParentFileName=FileName,InitiatingProcessG2ParentFolderPath=FolderPath,InitiatingProcessG2ParentSHA1=SHA1, InitiatingProcessG2ParentId=ProcessId, InitiatingProcessG2ParentCommandLine=ProcessCommandLine, InitiatingProcessG2ParentCreationTime=todatetime(ProcessCreationTime),
InitiatingProcessG3ParentFileName=InitiatingProcessFileName,InitiatingProcessG3ParentFolderPath=InitiatingProcessFolderPath,InitiatingProcessG3ParentSHA1=InitiatingProcessSHA1, InitiatingProcessG3ParentId=InitiatingProcessId, InitiatingProcessG3ParentCommandLine=InitiatingProcessCommandLine, InitiatingProcessG3ParentCreationTime=todatetime(InitiatingProcessCreationTime)
)
on DeviceId , InitiatingProcessG3ParentFileName, InitiatingProcessG3ParentId, InitiatingProcessG3ParentCreationTime
// 2nd iteration of join.
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(timeframe)
| project DeviceId, InitiatingProcessG1ParentFileName=FileName,InitiatingProcessG1ParentFolderPath=FolderPath,InitiatingProcessG1ParentSHA1=SHA1, InitiatingProcessG1ParentId=ProcessId, InitiatingProcessG1ParentCommandLine=ProcessCommandLine, InitiatingProcessG1ParentCreationTime=todatetime(ProcessCreationTime),
InitiatingProcessG2ParentFileName=InitiatingProcessFileName,InitiatingProcessG2ParentFolderPath=InitiatingProcessFolderPath,InitiatingProcessG2ParentSHA1=InitiatingProcessSHA1, InitiatingProcessG2ParentId=InitiatingProcessId, InitiatingProcessG2ParentCommandLine=InitiatingProcessCommandLine, InitiatingProcessG2ParentCreationTime=todatetime(InitiatingProcessCreationTime)
)
on DeviceId , InitiatingProcessG2ParentFileName , InitiatingProcessG2ParentId, InitiatingProcessG2ParentCreationTime
// 3rd iteration of join.
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(timeframe)
| project DeviceId, InitiatingProcessParentFileName=FileName,InitiatingProcessParentFolderPath=FolderPath,InitiatingProcessParentSHA1=SHA1, InitiatingProcessParentId=ProcessId, InitiatingProcessParentCommandLine=ProcessCommandLine, InitiatingProcessParentCreationTime=ProcessCreationTime,
InitiatingProcessG1ParentFileName=InitiatingProcessFileName,InitiatingProcessG1ParentFolderPath=InitiatingProcessFolderPath,InitiatingProcessG1ParentSHA1=InitiatingProcessSHA1, InitiatingProcessG1ParentId=InitiatingProcessId, InitiatingProcessG1ParentCommandLine=InitiatingProcessCommandLine, InitiatingProcessG1ParentCreationTime=todatetime(InitiatingProcessCreationTime)
)
on DeviceId , InitiatingProcessG1ParentFileName , InitiatingProcessG1ParentId, InitiatingProcessG1ParentCreationTime
// 4th iteration of join
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(timeframe)
| project DeviceId, InitiatingProcessFileName=FileName,InitiatingProcessSHA1=SHA1, InitiatingProcessId=ProcessId, InitiatingProcessCommandLine=ProcessCommandLine, InitiatingProcessCreationTime=ProcessCreationTime,
InitiatingProcessParentFileName=InitiatingProcessFileName,InitiatingProcessParentSHA1=InitiatingProcessSHA1, InitiatingProcessParentId=InitiatingProcessId, InitiatingProcessParentCommandLine=InitiatingProcessCommandLine, InitiatingProcessParentCreationTime=InitiatingProcessCreationTime
)
on DeviceId , InitiatingProcessParentFileName , InitiatingProcessParentId, InitiatingProcessParentCreationTime
// 5th iteration of join
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(timeframe)
| project Timestamp, DeviceId, FileName,SHA1, ProcessId, ProcessCommandLine, ProcessCreationTime,
InitiatingProcessFileName,InitiatingProcessSHA1, InitiatingProcessId, InitiatingProcessCommandLine, InitiatingProcessCreationTime
)
on DeviceId , InitiatingProcessFileName , InitiatingProcessId, InitiatingProcessCreationTime
);
// Use the cached results and find the rare patterns based on process names.
_process_tree_data
|summarize count() by FileName,InitiatingProcessFileName,InitiatingProcessParentFileName,InitiatingProcessG1ParentFileName,InitiatingProcessG2ParentFileName,InitiatingProcessG3ParentFileName,InitiatingProcessG4ParentFileName
| where count_ < 10 // If the count of a pattern is less than 10, it is anomalous. Threshold can be changed.
// Now, join the anomalous patterns with the original results to get the details.
| join kind=inner _process_tree_data on FileName,InitiatingProcessFileName,InitiatingProcessParentFileName,InitiatingProcessG1ParentFileName,InitiatingProcessG2ParentFileName,InitiatingProcessG3ParentFileName,InitiatingProcessG4ParentFileName
// Now, join the anomalous patterns with the original results to get the details.
|project Timestamp=case(isnotempty(Timestamp),Timestamp,isnotempty(InitiatingProcessParentCreationTime),InitiatingProcessParentCreationTime,isnotempty(InitiatingProcessG1ParentCreationTime),InitiatingProcessG1ParentCreationTime,
isnotempty(InitiatingProcessG2ParentCreationTime),InitiatingProcessG2ParentCreationTime,isnotempty(InitiatingProcessG3ParentCreationTime),InitiatingProcessG3ParentCreationTime,InitiatingProcessG4ParentCreationTime),
count_ , DeviceId, DeviceName,
InitiatingProcessG4ParentFileName,InitiatingProcessG3ParentFileName,InitiatingProcessG2ParentFileName,InitiatingProcessG1ParentFileName,InitiatingProcessParentFileName,InitiatingProcessFileName,FileName,
InitiatingProcessG4ParentCommandLine, InitiatingProcessG3ParentCommandLine, InitiatingProcessG2ParentCommandLine, InitiatingProcessG1ParentCommandLine, InitiatingProcessCommandLine, ProcessCommandLine,
InitiatingProcessG4ParentId, InitiatingProcessG4ParentCreationTime,
InitiatingProcessG3ParentId, InitiatingProcessG3ParentFolderPath ,InitiatingProcessG3ParentSHA1, InitiatingProcessG3ParentCreationTime,
InitiatingProcessG2ParentId,InitiatingProcessG2ParentFolderPath,InitiatingProcessG2ParentSHA1, InitiatingProcessG2ParentCreationTime,
InitiatingProcessG1ParentId,InitiatingProcessG1ParentFolderPath,InitiatingProcessG1ParentSHA1, InitiatingProcessG1ParentCreationTime,
InitiatingProcessParentId, InitiatingProcessParentFolderPath,InitiatingProcessParentSHA1, InitiatingProcessParentCommandLine ,InitiatingProcessParentCreationTime,
InitiatingProcessId, InitiatingProcessSHA1, InitiatingProcessCreationTime,
ProcessId, SHA1, ProcessCreationTime
| order by Timestamp, DeviceName, InitiatingProcessG4ParentCreationTime , InitiatingProcessG3ParentCreationTime , InitiatingProcessG2ParentCreationTime , InitiatingProcessG1ParentCreationTime , InitiatingProcessCreationTime
let _timeframe = 1d;
// define of which processes you want to generate process tree
let _selected_processes = dynamic(["winword.exe","excel.exe","powerpnt.exe","acrord32.exe", "FoxitPhantomPDF.exe","MicrosoftPdfReader.exe","SumatraPDF.exe", "onenote.exe"]);
// Sysmon logs are not parsed automatically, below function parses the Sysmon EventID=1 logs.
let parse_sysmon_1 = (T:(TimeGenerated:datetime,EventID:int, Source:string,RenderedDescription:string, EventData:string))
{
T
| where TimeGenerated > ago(_timeframe)
| where Source == "Microsoft-Windows-Sysmon" and 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(['@Name']), Value=['#text']
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
| extend RuleName = column_ifexists("RuleName", ""), TechniqueId = column_ifexists("TechniqueId", ""), TechniqueName = column_ifexists("TechniqueName", "")
| parse RuleName with * 'technique_id=' TechniqueId ',' * 'technique_name=' TechniqueName
};
// First, generate the process tree and store it in the cache
let _process_tree_data = materialize (
// First, get only the processes created by selected process
Event
| invoke parse_sysmon_1() //parsing the sysmon logs
// Get only the required fields. Rrenaming fields accordingly to generate a tree up to 5th level
| project Computer,G2_ParentProcessId=tostring(ProcessId),G2_ParentProcess=tostring(OriginalFileName),G2_ParentProcessGuid=tostring(ProcessGuid),G2_ParentProcessCommandLine=tostring(CommandLine),
G3_ParentProcessId=tostring(ParentProcessId),G3_ParentProcess=tostring(parse_path(tostring(ParentImage)).Filename),G3_ParentProcessGuid=tostring(ParentProcessGuid),G3_ParentProcessCommandLine=tostring(ParentCommandLine)
| where G3_ParentProcess in~ (_selected_processes)
// Start iteration
// First iteration of join. From now on, query all processes, rename fields, and join accordingly
| join kind=leftouter
(
Event
| invoke parse_sysmon_1()
| project G1_ParentProcessId=tostring(ProcessId),G1_ParentProcess=tostring(OriginalFileName),G1_ParentProcessGuid=tostring(ProcessGuid),G1_ParentProcessCommandLine=tostring(CommandLine),
G2_ParentProcessId=tostring(ParentProcessId),G2_ParentProcess=tostring(parse_path(tostring(ParentImage)).Filename),G2_ParentProcessGuid=tostring(ParentProcessGuid),G2_ParentProcessCommandLine=tostring(ParentCommandLine)
) on G2_ParentProcessGuid
// Second iteration of join.
| join kind=leftouter
(
Event
| invoke parse_sysmon_1()
| project ParentProcessId=tostring(ProcessId),ParentProcess=tostring(OriginalFileName),ParentProcessGuid=tostring(ProcessGuid),ParentProcessCommandLine=tostring(CommandLine),
G1_ParentProcessId=tostring(ParentProcessId),G1_ParentProcess=tostring(parse_path(tostring(ParentImage)).Filename),G1_ParentProcessGuid=tostring(ParentProcessGuid),G1_ParentProcessCommandLine=tostring(ParentCommandLine)
) on G1_ParentProcessGuid
// Third iteration of join.
| join kind=leftouter
(
Event
| invoke parse_sysmon_1()
| project ProcessId=tostring(ProcessId),Process=tostring(OriginalFileName),ProcessGuid=tostring(ProcessGuid),ProcessCommandLine=tostring(CommandLine),
ParentProcessId=tostring(ParentProcessId),ParentProcess=tostring(parse_path(tostring(ParentImage)).Filename),ParentProcessGuid=tostring(ParentProcessGuid),ParentProcessCommandLine=tostring(ParentCommandLine)
) on ParentProcessGuid
);
// Use the cached results and find the rare patterns based on process names.
_process_tree_data
| summarize count() by Process, ParentProcess, G1_ParentProcess, G2_ParentProcess, G3_ParentProcess
| where count_ < 10 // If the count of a pattern is less than 10, it is anomalous. Threshold can be changed.
// Now, join the anomalous patterns with the original results to get the details.
| join kind=inner _process_tree_data on Process, ParentProcess, G1_ParentProcess, G2_ParentProcess, G3_ParentProcess
| project-reorder Computer, count_, G3_ParentProcess, G2_ParentProcess, G1_ParentProcess, ParentProcess, Process,
G3_ParentProcessCommandLine, G2_ParentProcessCommandLine, G1_ParentProcessCommandLine, ParentProcessCommandLine, ProcessCommandLine,
G3_ParentProcessId, G2_ParentProcessId, G1_ParentProcessId, ParentProcessId, ProcessId
Stages and Predicates
Parameters
let timeframe = 48h;
let _selected_processes = dynamic(["winword.exe","excel.exe","powerpnt.exe","acrord32.exe", "FoxitPhantomPDF.exe","MicrosoftPdfReader.exe","SumatraPDF.exe", "onenote.exe"]);
let _process_tree_data is inlined into the numbered stages below.
Stage 1: source
let _process_tree_data
Stage 2: source
DeviceProcessEvents
Stage 3: where
where Timestamp > ago(172800s)
Stage 4: where
where InitiatingProcessFileName in~ ("FoxitPhantomPDF.exe", "MicrosoftPdfReader.exe", "SumatraPDF.exe", "acrord32.exe", "excel.exe", "onenote.exe", "powerpnt.exe", "winword.exe")
Stage 5: project
project DeviceId, DeviceName, InitiatingProcessG3ParentCommandLine, InitiatingProcessG3ParentCreationTime, InitiatingProcessG3ParentFileName, InitiatingProcessG3ParentId, InitiatingProcessG3ParentSHA1, InitiatingProcessG4ParentCommandLine, InitiatingProcessG4ParentCreationTime, InitiatingProcessG4ParentFileName, InitiatingProcessG4ParentId, InitiatingProcessG4ParentSHA1
Stage 6: join
join kind=leftouter (DeviceProcessEvents) on DeviceId, InitiatingProcessG3ParentFileName, InitiatingProcessG3ParentId, InitiatingProcessG3ParentCreationTime
Stage 7: join
join kind=leftouter (DeviceProcessEvents) on DeviceId, InitiatingProcessG2ParentFileName, InitiatingProcessG2ParentId, InitiatingProcessG2ParentCreationTime
Stage 8: join
join kind=leftouter (DeviceProcessEvents) on DeviceId, InitiatingProcessG1ParentFileName, InitiatingProcessG1ParentId, InitiatingProcessG1ParentCreationTime
Stage 9: join
join kind=leftouter (DeviceProcessEvents) on DeviceId, InitiatingProcessParentFileName, InitiatingProcessParentId, InitiatingProcessParentCreationTime
Stage 10: join
join kind=leftouter (DeviceProcessEvents) on DeviceId, InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime
Stage 11: summarize
summarize by FileName, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessG1ParentFileName, InitiatingProcessG2ParentFileName, InitiatingProcessG3ParentFileName, InitiatingProcessG4ParentFileName
Stage 12: where
where count_ < 10
Stage 13: join
join kind=inner (_process_tree_data) on FileName, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessG1ParentFileName, InitiatingProcessG2ParentFileName, InitiatingProcessG3ParentFileName, InitiatingProcessG4ParentFileName
Stage 14: project
project DeviceId, DeviceName, FileName, InitiatingProcessCommandLine, InitiatingProcessCreationTime, InitiatingProcessFileName, InitiatingProcessG1ParentCommandLine, InitiatingProcessG1ParentCreationTime, InitiatingProcessG1ParentFileName, InitiatingProcessG1ParentFolderPath, InitiatingProcessG1ParentId, InitiatingProcessG1ParentSHA1, InitiatingProcessG2ParentCommandLine, InitiatingProcessG2ParentCreationTime, InitiatingProcessG2ParentFileName, InitiatingProcessG2ParentFolderPath, InitiatingProcessG2ParentId, InitiatingProcessG2ParentSHA1, InitiatingProcessG3ParentCommandLine, InitiatingProcessG3ParentCreationTime, InitiatingProcessG3ParentFileName, InitiatingProcessG3ParentFolderPath, InitiatingProcessG3ParentId, InitiatingProcessG3ParentSHA1, InitiatingProcessG4ParentCommandLine, InitiatingProcessG4ParentCreationTime, InitiatingProcessG4ParentFileName, InitiatingProcessG4ParentId, InitiatingProcessId, InitiatingProcessParentCommandLine, InitiatingProcessParentCreationTime, InitiatingProcessParentFileName, InitiatingProcessParentFolderPath, InitiatingProcessParentId, InitiatingProcessParentSHA1, InitiatingProcessSHA1, ProcessCommandLine, ProcessCreationTime, ProcessId, SHA1, Timestamp, count_
Stage 15: sort
sort by DeviceName, InitiatingProcessCreationTime, InitiatingProcessG1ParentCreationTime, InitiatingProcessG2ParentCreationTime, InitiatingProcessG3ParentCreationTime, InitiatingProcessG4ParentCreationTime, Timestamp
Stage 16: source
let _process_tree_data
Stage 17: source
Event
Stage 18: where
where TimeGenerated > ago(86400s)
Stage 19: where
where EventID == 1 and Source =~ "Microsoft-Windows-Sysmon"
Stage 20: extend
extend RenderedDescription
Stage 21: extend
extend EventData
Stage 22: mv-expand
mv-expand EventData
Stage 23: evaluate
evaluate
Stage 24: extend
extend Key, Value
Stage 25: evaluate
evaluate
Stage 26: extend
extend RuleName, TechniqueId, TechniqueName
Stage 27: parse
parse
Stage 28: project
project Computer, G2_ParentProcess, G2_ParentProcessCommandLine, G2_ParentProcessGuid, G2_ParentProcessId, G3_ParentProcess, G3_ParentProcessCommandLine, G3_ParentProcessGuid, G3_ParentProcessId
Stage 29: where
where G3_ParentProcess in~ ("FoxitPhantomPDF.exe", "MicrosoftPdfReader.exe", "SumatraPDF.exe", "acrord32.exe", "excel.exe", "onenote.exe", "powerpnt.exe", "winword.exe")
Stage 30: join
join kind=leftouter (Event) on G2_ParentProcessGuid
Stage 31: join
join kind=leftouter (Event) on G1_ParentProcessGuid
Stage 32: join
join kind=leftouter (Event) on ParentProcessGuid
Stage 33: summarize
summarize by Process, ParentProcess, G1_ParentProcess, G2_ParentProcess, G3_ParentProcess
Stage 34: where
where count_ < 10
Stage 35: join
join kind=inner (_process_tree_data) on Process, ParentProcess, G1_ParentProcess, G2_ParentProcess, G3_ParentProcess
Stage 36: project-reorder
project-reorder
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventID | eq |
| field:"EventID" kind:eq value:"1" |
G3_ParentProcess | in |
| field:"G3_ParentProcess" kind:in |
InitiatingProcessFileName | in |
| field:"parent_process_name" kind:in |
count_ | lt |
| field:"count_" kind:lt value:"10" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceId | project |
DeviceName | project |
FileName | project |
InitiatingProcessCommandLine | project |
InitiatingProcessCreationTime | project |
InitiatingProcessFileName | project |
InitiatingProcessG1ParentCommandLine | project |
InitiatingProcessG1ParentCreationTime | project |
InitiatingProcessG1ParentFileName | project |
InitiatingProcessG1ParentFolderPath | project |
InitiatingProcessG1ParentId | project |
InitiatingProcessG1ParentSHA1 | project |
InitiatingProcessG2ParentCommandLine | project |
InitiatingProcessG2ParentCreationTime | project |
InitiatingProcessG2ParentFileName | project |
InitiatingProcessG2ParentFolderPath | project |
InitiatingProcessG2ParentId | project |
InitiatingProcessG2ParentSHA1 | project |
InitiatingProcessG3ParentCommandLine | project |
InitiatingProcessG3ParentCreationTime | project |
InitiatingProcessG3ParentFileName | project |
InitiatingProcessG3ParentFolderPath | project |
InitiatingProcessG3ParentId | project |
InitiatingProcessG3ParentSHA1 | project |
InitiatingProcessG4ParentCommandLine | project |
InitiatingProcessG4ParentCreationTime | project |
InitiatingProcessG4ParentFileName | project |
InitiatingProcessG4ParentId | project |
InitiatingProcessId | project |
InitiatingProcessParentCommandLine | project |
InitiatingProcessParentCreationTime | project |
InitiatingProcessParentFileName | project |
InitiatingProcessParentFolderPath | project |
InitiatingProcessParentId | project |
InitiatingProcessParentSHA1 | project |
InitiatingProcessSHA1 | project |
ProcessCommandLine | project |
ProcessCreationTime | project |
ProcessId | project |
SHA1 | project |
Timestamp | project |
count_ | project |