
Practical Temporal Proximity in KQL
A technical KQL deep-dive on detecting hands-on-keyboard attacker activity by correlating multiple suspicious process executions (route.exe, quser.exe, curl.exe, setspn.exe, klist.exe) within a time window on the same device. The author explains why bin()/bin_at() bucketing and Near-Real-Time (NRT) analytic rules are unreliable for temporal-proximity detections, then walks through using the row_window_session() window function with MaxDistanceFromFirst, MaxDistanceBetweenNeighbors, and a Restart expression to build accurate session-based correlation across DeviceProcessEvents in Microsoft Defender XDR.
What it is
A KQL detection-engineering technique for "temporal proximity" — correlating multiple related events within a bounded time window — built for Microsoft Defender XDR / Sentinel. The worked example detects APT hands-on-keyboard activity: at least 3 of route.exe, quser.exe, curl.exe, setspn.exe, klist.exe launched within 5 minutes of each other.
How it works
The core is the row_window_session window function, which groups rows into "session windows" keyed off a datetime column:
row_window_session(Expr, MaxDistanceFromFirst, MaxDistanceBetweenNeighbors [, Restart])- MaxDistanceFromFirst (timespan): the session starts at the first row's timestamp; rows within this span share that SessionID. A row past the span opens a new session.
- MaxDistanceBetweenNeighbors (timespan): also pulls in rows that are close to the previous row even if beyond the first-row distance — e.g.
row_window_session(Timestamp, 5m, 1m)captures a row 6m from row 1 but 1m from row 10. - Restart (optional boolean): forces a new SessionID when true, e.g.
DeviceName != prev(DeviceName)to keep sessions per-device.
Why the obvious approaches fail:
bin()/bin_at(): they round timestamps into fixed buckets with a predefined start point. Events genuinely close in time can straddle a bucket boundary and be counted separately.bin_at()lets you move the start but the boundary problem remains.- NRT analytic rules: useful for minute-0 high-severity alerts and may handle ~75% of cases via a 30-minute lookback summarize, but carry documented restrictions (see Sandor Tokesi / Senturean on NRT rule restrictions) that make them unfit as the general solution.
Preconditions / Affected
- Microsoft Defender XDR / MDE telemetry. Note the author's warning that some tables are unreliable for timing-based detections:
DeviceImageLoadEventswas found unusable for detecting libraries loaded in a specific order/timespan. - Data must be serialized before any window function is called — use the
serializeoperator (true for all window functions). Without this,row_window_sessionwon't behave.
Caveats
- The fetched page cuts off before the final assembled query ("No More Time for Talk: The Solution" begins but the KQL isn't shown here), so the complete detection logic — including how the 3-of-5 process threshold is applied to the resulting sessions — must be read on the source page.
- The 5-minute window and 3-command threshold are the author's tuning choices for a low false-positive rate against post-exploitation command patterns, not fixed values.
Technologies named here
- Microsoft Defender XDR
- KQL
- NRT analytic rules
- Microsoft Sentinel
Techniques named here
- Temporal Proximity detection
- Hands-on-keyboard / post-exploitation command detection
- bin / bin_at time bucketing
- row_window_session
- Serialization operator / window functions requirement
- Potential C2 beacon traffic correlation
Related in Blue Team & Detection
AI-Coded Malware | Analyzing Vibe-Coded AD Enumeration | Huntress
