scan
This is example from WPNinja Summit 2022 session "Throwing KQL like a shuriken". Presented by Gianni Castaldi and Alex Verboon
Example 1
// Find Evil
search "ntdsutil"
// Find Evil
DeviceImageLoadEvents
| where InitiatingProcessFileName =~ "ntdsutil.exe"
// Step one samlib.dll
DeviceImageLoadEvents
| where InitiatingProcessFileName =~ "ntdsutil.exe"
| sort by Timestamp asc
| scan with_match_id=funnel_id declare(Step:string, Delta:timespan) with
(
step Authentication: InitiatingProcessFileName =~ "ntdsutil.exe"
and FileName =~ "samlib.dll"
=> Step = "Authenticated";
)
// Step two add vss_ps.dll
DeviceImageLoadEvents
| where InitiatingProcessFileName =~ "ntdsutil.exe"
| sort by Timestamp asc
| scan with_match_id=funnel_id declare(Step:string, Delta:timespan) with
(
step Authentication: InitiatingProcessFileName =~ "ntdsutil.exe"
and FileName =~ "samlib.dll"
=> Step = "Authenticated";
step NTDSExport: InitiatingProcessFileName =~ "ntdsutil.exe"
and FileName =~ "vss_ps.dll"
and Authentication.Timestamp > 10m
=> Step = "NTDS export"
, Delta = Timestamp - Authentication.Timestamp;
)
| project-reorder Timestamp, DeviceId, DeviceName, funnel_id, FileName, Step
// Step three finalize all
DeviceImageLoadEvents
| where InitiatingProcessFileName =~ "ntdsutil.exe"
| sort by Timestamp asc
| scan with_match_id=funnel_id declare(Step:string, Delta:timespan) with
(
step Authentication: InitiatingProcessFileName =~ "ntdsutil.exe"
and FileName =~ "samlib.dll"
=> Step = "Authenticated";
step NTDSExport: InitiatingProcessFileName =~ "ntdsutil.exe"
and FileName =~ "vss_ps.dll"
and Authentication.Timestamp > 10m
=> Step = "NTDS export"
, Delta = Timestamp - Authentication.Timestamp;
)
| where Step == "NTDS export"
| project-reorder Timestamp, DeviceId, DeviceName, funnel_id, FileName, Step
Example 2
let SuspiciousFiles = dynamic([
"dir.exe"
, "ipconfig.exe"
, "systeminfo.exe"
, "ping.exe"
, "type.exe"
, "net.exe"
, "dsquery.exe"
, "csvde.exe"
, "nbtstat.exe"
, "nltest.exe"
, "ntdsutil.exe"
, "adfind.exe"
, "nslookup.exe"
, "procdump.exe"
, "whoami.exe"
, "wmic.exe"
, "mimikatz.exe"
, "tasklist.exe"
, "rubeus.exe"]);
DeviceProcessEvents
| where FileName has_any(SuspiciousFiles)
let SuspiciousFiles = dynamic(["dir.exe", "ipconfig.exe", "systeminfo.exe", "ping.exe", "type.exe", "net.exe"
, "dsquery.exe", "csvde.exe", "nbtstat.exe", "nltest.exe", "ntdsutil.exe", "adfind.exe", "nslookup.exe"
, "procdump.exe", "whoami.exe", "wmic.exe", "mimikatz.exe", "tasklist.exe", "rubeus.exe"]);
DeviceProcessEvents
| where FileName has_any(SuspiciousFiles)
| project Timestamp, DeviceId, FileName, ProcessCommandLine, ReportId
| sort by DeviceId asc, Timestamp asc
| scan with_match_id=funnel_id declare(Step:int, Delta:timespan, Duration:timespan, Files:dynamic, CommandLines:dynamic) with
(
step s1: FileName in(SuspiciousFiles)
=> Step = 1, Delta = timespan(0)
, Duration = timespan(0)
, Files = pack_array(FileName)
, CommandLines = pack_array(ProcessCommandLine);
)
| summarize arg_max(Timestamp, ReportId, *) by funnel_id, DeviceId, Step
let TimePeriod = 5m;
let Threshold = 3;
let SuspiciousFiles = dynamic(["dir.exe", "ipconfig.exe", "systeminfo.exe", "ping.exe", "type.exe", "net.exe"
, "dsquery.exe", "csvde.exe", "nbtstat.exe", "nltest.exe", "ntdsutil.exe", "adfind.exe", "nslookup.exe"
, "procdump.exe", "whoami.exe", "wmic.exe", "mimikatz.exe", "tasklist.exe", "rubeus.exe"]);
DeviceProcessEvents
| where FileName has_any(SuspiciousFiles)
| project Timestamp, DeviceId, FileName, ProcessCommandLine, ReportId
| sort by DeviceId asc, Timestamp asc
| scan with_match_id=funnel_id declare(Step:int, Delta:timespan, Duration:timespan, Files:dynamic, CommandLines:dynamic) with
(
step s1: FileName in(SuspiciousFiles)
=> Step = 1, Delta = timespan(0)
, Duration = timespan(0)
, Files = pack_array(FileName)
, CommandLines = pack_array(ProcessCommandLine);
step s2: FileName in(SuspiciousFiles)
and Timestamp - s1.Timestamp <= TimePeriod
and DeviceId == s1.DeviceId
and ProcessCommandLine != s1.ProcessCommandLine => Step = 2
, Delta = Timestamp - s1.Timestamp
, Duration = Timestamp - s1.Timestamp
, Files = pack_array(s1.FileName, FileName)
, CommandLines = pack_array(s1.ProcessCommandLine, ProcessCommandLine);
step s3: FileName in(SuspiciousFiles) and Timestamp - s2.Timestamp <= TimePeriod and DeviceId == s2.DeviceId and ProcessCommandLine != s1.ProcessCommandLine and ProcessCommandLine != s2.ProcessCommandLine => Step = 3, Delta = Timestamp - s2.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, s2.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, s2.ProcessCommandLine, ProcessCommandLine);
step s4: FileName in(SuspiciousFiles) and Timestamp - s3.Timestamp <= TimePeriod and DeviceId == s3.DeviceId and ProcessCommandLine != s1.ProcessCommandLine and ProcessCommandLine != s2.ProcessCommandLine and ProcessCommandLine != s3.ProcessCommandLine => Step = 4, Delta = Timestamp - s3.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, s2.FileName, s3.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, s2.ProcessCommandLine, s3.ProcessCommandLine, ProcessCommandLine);
step s5: FileName in(SuspiciousFiles) and Timestamp - s4.Timestamp <= TimePeriod and DeviceId == s4.DeviceId and ProcessCommandLine != s1.ProcessCommandLine and ProcessCommandLine != s2.ProcessCommandLine and ProcessCommandLine != s3.ProcessCommandLine and ProcessCommandLine != s4.ProcessCommandLine => Step = 5, Delta = Timestamp - s4.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, s2.FileName, s3.FileName, s4.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, s2.ProcessCommandLine, s3.ProcessCommandLine, s4.ProcessCommandLine, ProcessCommandLine);
)
| summarize arg_max(Timestamp, ReportId, *) by funnel_id, DeviceId, Step
let TimePeriod = 5m;
let Threshold = 3;
let SuspiciousFiles = dynamic(["dir.exe"
, "ipconfig.exe"
, "systeminfo.exe"
, "ping.exe"
, "type.exe"
, "net.exe"
, "dsquery.exe"
, "csvde.exe"
, "nbtstat.exe"
, "nltest.exe"
, "ntdsutil.exe"
, "adfind.exe"
, "nslookup.exe"
, "procdump.exe"
, "whoami.exe"
, "wmic.exe"
, "mimikatz.exe"
, "tasklist.exe"
, "rubeus.exe"
]);
let ExcludedCommands = dynamic(["wuauserv"
]);
DeviceProcessEvents
| where FileName has_any(SuspiciousFiles)
| where not(ProcessCommandLine has_any(ExcludedCommands))
| where InitiatingProcessParentFileName != "MsSense.exe" and InitiatingProcessParentFileName != "SenseIR.exe" and InitiatingProcessFileName != "MsSense.exe"
| project Timestamp, DeviceId, FileName, ProcessCommandLine, ReportId
| sort by DeviceId asc, Timestamp asc
| scan with_match_id=funnel_id declare(Step:int, Delta:timespan, Duration:timespan, Files:dynamic, CommandLines:dynamic) with
(
step s1: FileName in(SuspiciousFiles) => Step = 1, Delta = timespan(0), Duration = timespan(0), Files = pack_array(FileName), CommandLines = pack_array(ProcessCommandLine);
step s2: FileName in(SuspiciousFiles) and Timestamp - s1.Timestamp <= TimePeriod and DeviceId == s1.DeviceId and ProcessCommandLine != s1.ProcessCommandLine => Step = 2, Delta = Timestamp - s1.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, ProcessCommandLine);
step s3: FileName in(SuspiciousFiles) and Timestamp - s2.Timestamp <= TimePeriod and DeviceId == s2.DeviceId and ProcessCommandLine != s1.ProcessCommandLine and ProcessCommandLine != s2.ProcessCommandLine => Step = 3, Delta = Timestamp - s2.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, s2.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, s2.ProcessCommandLine, ProcessCommandLine);
step s4: FileName in(SuspiciousFiles) and Timestamp - s3.Timestamp <= TimePeriod and DeviceId == s3.DeviceId and ProcessCommandLine != s1.ProcessCommandLine and ProcessCommandLine != s2.ProcessCommandLine and ProcessCommandLine != s3.ProcessCommandLine => Step = 4, Delta = Timestamp - s3.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, s2.FileName, s3.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, s2.ProcessCommandLine, s3.ProcessCommandLine, ProcessCommandLine);
step s5: FileName in(SuspiciousFiles) and Timestamp - s4.Timestamp <= TimePeriod and DeviceId == s4.DeviceId and ProcessCommandLine != s1.ProcessCommandLine and ProcessCommandLine != s2.ProcessCommandLine and ProcessCommandLine != s3.ProcessCommandLine and ProcessCommandLine != s4.ProcessCommandLine => Step = 5, Delta = Timestamp - s4.Timestamp, Duration = Timestamp - s1.Timestamp, Files = pack_array(s1.FileName, s2.FileName, s3.FileName, s4.FileName, FileName), CommandLines = pack_array(s1.ProcessCommandLine, s2.ProcessCommandLine, s3.ProcessCommandLine, s4.ProcessCommandLine, ProcessCommandLine);
)
| summarize arg_max(Timestamp, ReportId, *) by funnel_id, DeviceId, Step
| where Step >= Threshold
Last updated