KQL • Microsoft Defender XDR • Sentinel

KQL Threat Hunting in the Real World

Dashboards help. Alerts help. But real investigations usually begin one level deeper. At GEMXIT, KQL is how we validate suspicious activity, hunt weak signals, pivot across identity, endpoint, email and network telemetry, and work out what really happened.

This is where security operations becomes practical. Good hunting is not about writing fancy queries for the sake of it. It is about asking the right question, following the evidence, and building detections that reflect what attackers actually do.

Agent Foskett KQL threat hunting briefing
Briefing summary

KQL is where weak signals become visible. GEMXIT uses it to investigate suspicious PowerShell, risky sign-ins, spoofed email, persistence, reconnaissance and unusual outbound activity across Microsoft security tooling.

Validate low-confidence signals
Pivot across Microsoft telemetry
Turn hunts into detections

What good KQL hunting actually looks like

Good hunting is not random searching. It is structured investigation driven by behaviour, context and pivots.
Start with the behaviour We do not begin with “What query should we run?” We begin with “What looks wrong?” Suspicious PowerShell, sign-in anomalies, odd email authentication, strange network activity, or persistence changes.
Pivot, do not stop Email to user. User to sign-in. Sign-in to endpoint. Endpoint to process. Process to network. That is how isolated events become a timeline.
Turn patterns into coverage If the same hunt keeps proving useful, it should eventually become a custom detection, analytic rule, or at least a standard triage query.

A practical starting hunt

This is the type of KQL GEMXIT would use to quickly surface suspicious PowerShell, risky sign-ins and unusual email patterns in the same investigation window.
initial-hunt-pivot.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
let SuspiciousPowerShell =
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any ("-enc", "-encodedcommand", "FromBase64String", "-w hidden", "bypass", "downloadstring", "iex")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName;

let RiskySignins =
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| where RiskLevelDuringSignIn !in ("none", "hidden")
   or RiskEventTypes_V2 has_any ("unlikelyTravel", "anonymizedIPAddress", "maliciousIPAddress", "unfamiliarFeatures")
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, RiskLevelDuringSignIn, RiskEventTypes_V2;

let SpoofedEmailSignals =
EmailEvents
| where Timestamp > ago(7d)
| where AuthenticationDetails has_any ("spf=fail", "dkim=fail", "dmarc=fail")
| project Timestamp, SenderFromAddress, RecipientEmailAddress, Subject, AuthenticationDetails, ThreatTypes;

SuspiciousPowerShell
| order by Timestamp desc
What this gives you A quick triage set covering suspicious code execution, sign-in risk, and mail authentication failures in the same time window.
Why it matters This is how weak signals start joining up. A suspicious email, followed by a risky sign-in, followed by PowerShell, is no longer three separate events.
How GEMXIT uses it As a starting point only. The real value comes from the pivots that follow, based on who, what, where and when.

Real KQL examples we actually care about

These are the kinds of queries that help move an investigation forward, not just generate noise.
PowerShell launched by Office Useful for checking whether Word, Excel or Outlook triggered PowerShell after a malicious document or user click.
Failed sign-ins followed by success Helpful for spotting password spraying, guessing, or brute-force style access attempts that eventually worked.
Spoofing and mail auth failures Useful when a message looks internal or trusted but SPF, DKIM or DMARC tell a different story.
Persistence checks Registry run keys, scheduled tasks and suspicious startup changes matter because attackers usually want a way back in.
Reconnaissance activity Commands like whoami, net, nltest, quser and ipconfig often help reveal the early hands-on-keyboard phase.
Suspicious outbound traffic If a questionable process is followed by an outbound connection, the investigation usually gets much more interesting.

Example: PowerShell launched by Office or browser activity

One of the most useful practical hunts. It helps answer a simple question: did a user action in Office or the browser lead directly to script execution?
office-to-powershell-pivot.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where InitiatingProcessFileName in~ ("winword.exe", "excel.exe", "outlook.exe", "chrome.exe", "msedge.exe")
| project
    Timestamp,
    DeviceName,
    AccountName,
    InitiatingProcessFileName,
    FileName,
    ProcessCommandLine,
    InitiatingProcessCommandLine
| order by Timestamp desc
Used for Phishing follow-up validation, suspicious macro behaviour, and browser-led script execution after malicious downloads or fake prompts.
What to look for Encoded commands, hidden windows, downloadstring, iex, policy bypasses, and command lines that do not belong in normal business activity.
Why it matters It closes the gap between “someone clicked something” and “code actually ran”. That is often the missing step in early triage.

Example: failed sign-ins followed by success

Very useful for spotting low-noise password attacks and accounts that may have eventually been accessed successfully.
failed-then-success-signin.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
let Failed =
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType != 0
| summarize FailedCount = count() by UserPrincipalName, IPAddress, bin(TimeGenerated, 15m);

let Success =
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| summarize SuccessCount = count() by UserPrincipalName, IPAddress, bin(TimeGenerated, 15m);

Failed
| join kind=inner Success on UserPrincipalName, IPAddress, TimeGenerated
| where FailedCount >= 5
Used for Password spray checks, repeated login attempts, and validating whether persistent failures finally turned into a successful sign-in.
Best pivot Take the user and IP, then pivot into Conditional Access result, location, app, MFA context, and device activity around the same time.
Why it matters Repeated failure is noise. Repeated failure followed by success is often the moment the investigation changes shape.

Example: persistence and suspicious outbound activity

Once suspicious execution is confirmed, the next questions are usually: did it persist, and did it talk to anything?
persistence-and-network-pivot.kql
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
DeviceRegistryEvents
| where Timestamp > ago(7d)
| where RegistryKey has_any (
    @"\Software\Microsoft\Windows\CurrentVersion\Run",
    @"\Software\Microsoft\Windows\CurrentVersion\RunOnce"
)
| project Timestamp, DeviceName, AccountName, RegistryKey, RegistryValueName, RegistryValueData, InitiatingProcessFileName;

DeviceNetworkEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("powershell.exe", "pwsh.exe", "cmd.exe", "wscript.exe", "mshta.exe", "rundll32.exe")
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort, RemoteUrl
Used for Checking whether a suspicious process established persistence and whether it attempted external communications that suggest staging or command-and-control.
Best pivot Take the device and account into process creation history, scheduled tasks, service creation, downloaded files and Defender alerts in the same window.
Why it matters Execution is one stage. Persistence and outbound traffic are where an isolated event starts looking like a real intrusion path.

How GEMXIT uses KQL day to day

The point is not to write clever queries. The point is to hunt efficiently, understand intent, and reduce uncertainty.
Validate alerts that look too small Informational and low-confidence events are often where a real timeline begins. KQL helps separate noise from the first sign of something meaningful.
Investigate with pivots, not assumptions We move between email, user, sign-in, device, process and network telemetry quickly. That is usually how the real story appears.
Build stronger detections over time Once a query repeatedly proves useful, it should inform detection engineering, Sentinel analytics, Defender custom detections, or standard response playbooks.
Good KQL does more than return data.
It helps explain what happened, what matters, and what needs to happen next.
Contact GEMXIT

Final thought

The best Microsoft security environments do not just collect telemetry. They know how to interrogate it properly.
At GEMXIT We use KQL to hunt beyond the obvious, validate suspicious activity properly, and build practical Microsoft security visibility across Defender XDR, Sentinel, Entra ID and Microsoft 365.
Agent Foskett mindset The important question is rarely “Did we get an alert?” It is usually “What really happened, and what does the telemetry say when we go looking properly?”

If you want help improving Microsoft security visibility, threat hunting and detection quality, 👉 review Microsoft security operations

Develop IT. Protect IT. GEMXIT PTY LTD | GEMXIT UK LTD

KQL Threat Hunting in Microsoft Defender and Sentinel

This article explains how GEMXIT uses KQL in real-world Microsoft security investigations to hunt suspicious activity across endpoint, identity, email and network telemetry.

Practical KQL Queries for Microsoft Security Operations

Examples include suspicious PowerShell, risky sign-ins, spoofed email authentication failures, persistence checks and suspicious outbound connections.

How GEMXIT Uses KQL to Hunt and Detect Suspicious Activity

GEMXIT uses KQL to validate weak signals, investigate timelines properly and improve detection logic across Defender XDR, Microsoft Sentinel, Entra ID and Microsoft 365.