mirror of
https://github.com/marcredhat/kql
synced 2026-06-08 21:27:09 +00:00
Initial commit: KQL ↔ SDL PowerQuery proof of equivalence
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
// Same 90-day cross-source hunt expressed in SentinelOne SDL PowerQuery.
|
||||
//
|
||||
// Paste into a SDL tenant (e.g. https://xdr.us1.sentinelone.net) with
|
||||
// `startTime = "90d"`. Replace the synthetic domain regex if you have
|
||||
// a real IOC list.
|
||||
//
|
||||
// Why this looks different from the KQL:
|
||||
// * one schema (OCSF) instead of three runtime parser unions
|
||||
// * one engine path; 90d is more epochs scanned in parallel, NOT a
|
||||
// different code path (no Basic/Auxiliary/Archive tiers)
|
||||
// * the join is a single named-input join; no shuffle hint required
|
||||
// because reduction is already distributed
|
||||
//
|
||||
// NOTE on `loginIsSuccessful = 'false'`:
|
||||
// SDL stores booleans as lowercase strings. On a tenant whose OCSF
|
||||
// parser emits true/false as native booleans, drop the quotes:
|
||||
// AND event.login.loginIsSuccessful = false
|
||||
|
||||
| join
|
||||
failed_signins = (
|
||||
event.category = 'logins'
|
||||
AND event.login.loginIsSuccessful = 'false'
|
||||
| columns userName = event.login.userName,
|
||||
host = endpoint.name
|
||||
| group n_fails = count() by userName, host
|
||||
),
|
||||
bad_dns = (
|
||||
event.type = 'DNS Resolved'
|
||||
AND dns.question.name matches '(c2|suspect)\.example\.'
|
||||
| columns userName = src.endpoint.user.name,
|
||||
host = endpoint.name,
|
||||
domain = dns.question.name
|
||||
| group dns_hits = count(),
|
||||
domains = array_agg_distinct(domain, 20)
|
||||
by userName, host
|
||||
),
|
||||
susp_proc = (
|
||||
event.type = 'Process Creation'
|
||||
AND src.process.cmdline matches '(?i)(powershell|rundll32|mshta)'
|
||||
| columns userName = src.process.user,
|
||||
host = endpoint.name,
|
||||
cmdline = src.process.cmdline
|
||||
| group proc_hits = count(),
|
||||
cmdlines = array_agg_distinct(cmdline, 20)
|
||||
by userName, host
|
||||
)
|
||||
on userName, host
|
||||
| columns userName,
|
||||
host,
|
||||
hits = n_fails + dns_hits + proc_hits,
|
||||
n_fails,
|
||||
dns_hits,
|
||||
proc_hits,
|
||||
domains,
|
||||
cmdlines
|
||||
// If your tenant complains about bare field names after the join, use the
|
||||
// fully-prefixed form instead:
|
||||
// | columns userName, host,
|
||||
// hits = failed_signins.n_fails + bad_dns.dns_hits + susp_proc.proc_hits,
|
||||
// failed_signins.n_fails, bad_dns.dns_hits, susp_proc.proc_hits,
|
||||
// bad_dns.domains, susp_proc.cmdlines
|
||||
| sort -hits
|
||||
| limit 100
|
||||
Reference in New Issue
Block a user