Detection rules › Kusto

Process Tree Analysis

Group by
DeviceId, FileName, InitiatingProcessCreationTime, InitiatingProcessFileName, InitiatingProcessG1ParentCreationTime, InitiatingProcessG1ParentFileName, InitiatingProcessG1ParentId, InitiatingProcessG2ParentCreationTime, InitiatingProcessG2ParentFileName, InitiatingProcessG2ParentId, InitiatingProcessG3ParentCreationTime, InitiatingProcessG3ParentFileName, InitiatingProcessG3ParentId, InitiatingProcessG4ParentFileName, InitiatingProcessId, InitiatingProcessParentCreationTime, InitiatingProcessParentFileName, InitiatingProcessParentId
Author
Cyb3rMonk
Source
github.com/Cyb3r-Monk/Threat-Hunting-and-Detection

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.

FieldKindValuesSearch
EventIDeq
  • 1 corpus 241 (splunk 225, kusto 15, elastic 1)
field:"EventID" kind:eq value:"1"
G3_ParentProcessin
  • FoxitPhantomPDF.exe
  • MicrosoftPdfReader.exe
  • SumatraPDF.exe
  • acrord32.exe
  • excel.exe
  • onenote.exe
  • powerpnt.exe
  • winword.exe
field:"G3_ParentProcess" kind:in
InitiatingProcessFileNamein
  • FoxitPhantomPDF.exe corpus 3 (elastic 3)
  • MicrosoftPdfReader.exe
  • SumatraPDF.exe
  • acrord32.exe corpus 3 (elastic 3)
  • excel.exe corpus 28 (elastic 27, splunk 1)
  • onenote.exe corpus 3 (elastic 3)
  • powerpnt.exe corpus 23 (elastic 23)
  • winword.exe corpus 26 (elastic 26)
field:"parent_process_name" kind:in
count_lt
  • 10
field:"count_" kind:lt value:"10"

Output fields

These fields are emitted when the rule matches.

FieldSource
DeviceIdproject
DeviceNameproject
FileNameproject
InitiatingProcessCommandLineproject
InitiatingProcessCreationTimeproject
InitiatingProcessFileNameproject
InitiatingProcessG1ParentCommandLineproject
InitiatingProcessG1ParentCreationTimeproject
InitiatingProcessG1ParentFileNameproject
InitiatingProcessG1ParentFolderPathproject
InitiatingProcessG1ParentIdproject
InitiatingProcessG1ParentSHA1project
InitiatingProcessG2ParentCommandLineproject
InitiatingProcessG2ParentCreationTimeproject
InitiatingProcessG2ParentFileNameproject
InitiatingProcessG2ParentFolderPathproject
InitiatingProcessG2ParentIdproject
InitiatingProcessG2ParentSHA1project
InitiatingProcessG3ParentCommandLineproject
InitiatingProcessG3ParentCreationTimeproject
InitiatingProcessG3ParentFileNameproject
InitiatingProcessG3ParentFolderPathproject
InitiatingProcessG3ParentIdproject
InitiatingProcessG3ParentSHA1project
InitiatingProcessG4ParentCommandLineproject
InitiatingProcessG4ParentCreationTimeproject
InitiatingProcessG4ParentFileNameproject
InitiatingProcessG4ParentIdproject
InitiatingProcessIdproject
InitiatingProcessParentCommandLineproject
InitiatingProcessParentCreationTimeproject
InitiatingProcessParentFileNameproject
InitiatingProcessParentFolderPathproject
InitiatingProcessParentIdproject
InitiatingProcessParentSHA1project
InitiatingProcessSHA1project
ProcessCommandLineproject
ProcessCreationTimeproject
ProcessIdproject
SHA1project
Timestampproject
count_project