Threat Hunting

· 7 min read · Onion Infosec Editorial

Practical threat hunting in Microsoft security environments

A practical method for threat hunting in Microsoft Defender XDR and Microsoft Sentinel, with two KQL examples and guidance on recording every outcome.

In this article
  1. Hunting is hypothesis testing, not browsing
  2. Where the data lives
  3. A structure for every hunt
  4. Example 1: Office applications starting script interpreters
  5. Example 2: password spraying followed by a successful sign-in
  6. Turning a hunt into a detection
  7. Recording a hunt that found nothing
  8. Where to start

Blog

Threat Hunting
20 September 2026
7 min read

All articles

Microsoft security environments give analysts a query language and a large amount of data. That makes it easy to open advanced hunting, run a few interesting queries and call it threat hunting. Without a question to answer, that activity produces neither findings nor confidence.

Threat hunting is the search for activity that existing detections did not catch. It is useful when it is structured and when its results, including the empty ones, are written down. This article describes a method for Microsoft Defender XDR and Microsoft Sentinel, with two KQL examples.

Hunting is hypothesis testing, not browsing

A hunt begins with a statement that can be proved wrong. "Look for anything odd in PowerShell" is not one. "If a malicious document was opened, an Office application will have started a script interpreter on at least one device in the last 30 days" is. It names a technique, an observable, a data source and a time range.

Good hypotheses come from a small number of places:

  • MITRE ATT&CK techniques that matter to the environment and have no detection coverage
  • threat intelligence on how relevant actors operate, reduced to specific behaviors
  • recent incidents and near misses, especially the steps no rule caught
  • changes in the environment, such as a new remote access tool or a newly connected tenant
  • findings from penetration tests and red team exercises

A hunt is not a detection. A detection runs unattended and has to be precise. A hunt is run by a person who can tolerate noisy results and apply judgment, so it can look for weaker signals.

Where the data lives

Most hunts in a Microsoft environment use two stores. Knowing what each table contains, and what it does not, saves time.

Store Table What it holds
Defender XDR DeviceProcessEvents Process creation with command lines and parent process details
Defender XDR DeviceNetworkEvents Network connections, attributed to the initiating process
Defender XDR DeviceLogonEvents Logons on onboarded devices
Defender XDR EmailEvents Mail flow and delivery verdicts from Defender for Office 365
Defender XDR IdentityLogonEvents Authentication seen by Defender for Identity and Defender for Cloud Apps
Defender XDR CloudAppEvents Activity in Microsoft 365 and connected cloud applications
Sentinel SigninLogs Interactive Microsoft Entra ID sign-ins
Sentinel AuditLogs Entra ID directory changes such as consents, role assignments and credentials
Sentinel OfficeActivity Exchange Online, SharePoint and Teams audit records
Sentinel SecurityEvent Windows security events from servers with the agent, including domain controllers

Advanced hunting in Defender XDR keeps 30 days of data. Retention in Sentinel depends on how the workspace is configured and is usually longer, which matters for hunts that look further back.

One practical detail: Defender XDR tables use Timestamp, while Sentinel tables use TimeGenerated. A query copied from one to the other often needs only that change. Getting the connectors, retention and table coverage right is part of Microsoft security engineering, and hunting quality depends on it.

A structure for every hunt

  1. Hypothesis from a technique. Write it as one sentence, with the technique reference.
  2. Data check. Confirm the table exists, holds data for the whole period and covers the population. Compare the number of devices reporting to DeviceProcessEvents with the number of devices that exist. A hunt across partial data proves little, and the coverage gap is a finding in itself.
  3. Query. Start broad, summarize, then narrow. Record every exclusion with its reason.
  4. Triage. For each remaining result, establish who, which device, and what happened before and after. Pivot into other tables.
  5. Outcome. Confirmed malicious activity goes to incident response. Otherwise the hunt ends in a detection, a gap or a recorded negative result.

Set a time limit. A hunt that has no end date drifts back into browsing.

Example 1: Office applications starting script interpreters

The hypothesis: a malicious document or attachment caused an Office application to start a script interpreter or command shell.

DeviceProcessEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("winword.exe", "excel.exe", "powerpnt.exe", "outlook.exe")
| where FileName in~ ("powershell.exe", "pwsh.exe", "cmd.exe", "wscript.exe", "cscript.exe", "mshta.exe")
| summarize Executions = count(), Devices = dcount(DeviceName), Accounts = dcount(AccountName)
    by InitiatingProcessFileName, FileName, ProcessCommandLine
| sort by Devices asc

The query groups identical command lines and counts the devices and accounts that ran each one. Read from the top. Command lines seen on one or two devices are the ones to examine. Rows with many devices are usually a business add-in or a macro-driven finance tool, and they become exclusions once the owner confirms them.

In the command line, look for encoded commands, download activity and paths under temporary or user-writable folders. For a suspicious row, return to the raw events for that device and read InitiatingProcessCommandLine to see which document was open. Then check DeviceNetworkEvents for connections made by the child process, using InitiatingProcessFileName, RemoteUrl and RemoteIP.

Example 2: password spraying followed by a successful sign-in

The hypothesis: someone is trying common passwords across many accounts, and at least one attempt succeeded.

let SprayingIPs = SigninLogs
    | where TimeGenerated > ago(7d)
    | where ResultType == "50126"
    | summarize FailedAccounts = dcount(UserPrincipalName), Failures = count() by IPAddress
    | where FailedAccounts > 10;
SprayingIPs
| join kind=inner (
    SigninLogs
    | where TimeGenerated > ago(7d)
    | where ResultType == "0"
    | project TimeGenerated, UserPrincipalName, IPAddress, Location, AppDisplayName, ClientAppUsed
  ) on IPAddress
| project TimeGenerated, IPAddress, FailedAccounts, Failures, UserPrincipalName, Location, AppDisplayName, ClientAppUsed

The first part finds IP addresses with invalid username or password failures (result code 50126) against more than ten distinct accounts. The threshold is a starting point to adjust. The join returns successful sign-ins, result code 0, from the same addresses.

Many results will be shared egress points: an office network, a VPN concentrator or a cloud proxy, where users mistype passwords and also sign in successfully. Exclude those ranges and note why. What remains is a successful sign-in from an unfamiliar address that also failed against many accounts.

For those rows, check ClientAppUsed for legacy protocols, compare Location with the user's normal pattern, and review what the account did next in AuditLogs and OfficeActivity. A correct password that was then stopped by MFA or conditional access is logged with a different result code. It deserves a second pass, because the attacker still knows the password.

Turning a hunt into a detection

When a hunt query returns a manageable set of results and its exclusions are stable, it is a candidate detection. In Defender XDR it becomes a custom detection rule. In Sentinel it becomes a scheduled analytics rule. Several things change on the way.

  • Return individual events, not summaries. Custom detection rules expect row-level results with specific identifying columns, so the summarize step usually comes out.
  • Move exclusions out of the query text into a maintained list, such as a Sentinel watchlist, with an owner and a review date.
  • Map the rule to its ATT&CK technique, set a severity and write a short triage note for the analyst who will receive the alert.
  • Run the rule over historical data to estimate alert volume, then test it by performing the technique in a lab.
  • Review the rule after a few weeks of alerts and adjust it or retire it.

This is the point where hunting hands over to detection engineering. A hunting program that never produces detections is repeating the same manual work. A SOC that never hunts only finds what its rules already describe.

Recording a hunt that found nothing

An empty result is a result, but only if it is recorded with enough detail to be trusted later. For every hunt, keep:

  • the hypothesis and the technique it tests
  • the tables, the time range and the coverage of the data
  • the exact queries, with each exclusion and its reason
  • what was returned and how it was triaged
  • the outcome and any follow-up: a detection created, a logging gap raised, or none
  • the date, the hunter and when the hunt should be repeated

"Nothing found in 30 days of process data from the devices that report" is a bounded statement. It is not the same as "the environment is clean", and the record should not suggest that it is.

Sentinel's hunts and bookmarks can hold this record. A wiki page or a ticket works as well. What matters is that the next hunter can find it. Over time the record shows which techniques have been examined, how recently, and which cannot be examined because the data is missing.

Where to start

  1. List the tables you have, their retention and how much of the device and user population each one covers.
  2. Pick three techniques with no detection coverage and write one falsifiable hypothesis for each.
  3. Run the two example queries, tune the exclusions for your environment and record the outcomes.
  4. Create a one-page hunt record template and a single place to store completed hunts.
  5. Reserve fixed hunting time in the SOC schedule, and review each quarter which hunts became detections.

Filed underthreat huntingkqlmicrosoft sentineldefender xdrdetection engineering

Related capabilities

Where this becomes work.

The parts of Onion Infosec that deal with what this article describes.