A useful threat hunt connects a hypothesis to evidence. This follow-up to our PowerShell threat-hunting guide adds two investigation patterns, each with a Microsoft Defender XDR KQL query and a Splunk SPL equivalent: low-prevalence parent processes and network activity from the same PowerShell process instance.
Before you run anything: These are read-only teaching examples, not tested production detections. They have not been executed against a live Defender tenant or Splunk deployment. Validate fields, permissions, time handling, query limits and expected results in an authorized environment. A query match is a lead, not a compromise verdict.
Telemetry and field assumptions
KQL uses Defender for Endpoint data in DeviceProcessEvents and DeviceNetworkEvents. SPL assumes Windows Sysmon events forwarded to Splunk, with extracted fields named EventCode, Computer, Image, ParentImage, CommandLine and ProcessGuid. Replace YOUR_ENDPOINT_INDEX and the sourcetype with your actual values; these examples do not use the Splunk CIM data model.
Sysmon event 1 describes process creation; event 3 records network connections and is disabled by default. Confirm collection and parsing before interpreting an empty result. The endpoint field Computer must identify the originating device, not a forwarding server. Verify that Splunk _time reflects event time. Microsoft documents these telemetry sources in the Sysmon reference and the Defender process schema.
Hunt 1: low-prevalence PowerShell parents
Hypothesis: A parent process that starts PowerShell on very few monitored devices may reveal a workflow worth investigating. We group seven days of executions by parent name, then retain parents observed on three or fewer devices. The threshold is illustrative. It measures prevalence in the observed dataset, not first-seen status, global rarity or maliciousness.
KQL — Defender XDR
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where isnotempty(InitiatingProcessFileName)
| extend Parent = tolower(InitiatingProcessFileName)
| summarize Executions=count(), Devices=dcount(DeviceId),
FirstSeen=min(Timestamp), LastSeen=max(Timestamp),
ExampleDevices=make_set(DeviceName, 10) by Parent
| where Devices <= 3
| order by Devices asc, Executions ascSPL — Sysmon process creation
index=YOUR_ENDPOINT_INDEX sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" earliest=-7d latest=now EventCode=1
(Image="*\\powershell.exe" OR Image="*\\pwsh.exe")
| where isnotnull(Computer) AND isnotnull(ParentImage)
| eval Parent=lower(mvindex(split(ParentImage,"\\"),-1))
| stats count AS Executions dc(Computer) AS Devices
min(_time) AS FirstSeen max(_time) AS LastSeen by Parent
| where Devices <= 3
| convert ctime(FirstSeen) ctime(LastSeen)
| sort 0 Devices ExecutionsThe SPL example extracts the parent filename from its Windows path to approximate the KQL grouping. Validate path formats and field casing locally. KQL dcount is an approximate distinct count, so do not require perfect numerical parity with Splunk dc near your threshold. Neither query restricts the fleet to comparable device roles; scope it to an appropriate workstation or server cohort before operational use.
Investigate a match: Pivot to raw executions for that parent and device. Review parent path, process command, account, timestamp, relevant file evidence and the full process tree. Confirm the executable identity with available signing and hash evidence rather than trusting its name. Check deployment and support records.
Simulated example: A finance reporting utility launches PowerShell on two analyst machines. That is low prevalence, but an approved integration and matching change record may explain it. An unfamiliar binary using the same filename would require separate review. Do not allowlist the filename across the entire organization.
Hunt 2: network events within five minutes of process creation
Hypothesis: PowerShell network activity shortly after process creation can help reconstruct what an unusual execution did next. We correlate process and network evidence instead of assuming two events on one host belong together.
KQL — correlate device, PID and creation time
let PS = DeviceProcessEvents
| where Timestamp > ago(1d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where isnotnull(ProcessCreationTime)
| project DeviceId, DeviceName, ProcessId, ProcessCreationTime,
AccountName, Parent=InitiatingProcessFileName, ProcessCommandLine;
DeviceNetworkEvents
| where Timestamp > ago(1d)
| where InitiatingProcessFileName in~ ("powershell.exe", "pwsh.exe")
| project DeviceId, ProcessId=InitiatingProcessId,
ProcessCreationTime=InitiatingProcessCreationTime,
NetworkTime=Timestamp, ActionType, RemoteIP, RemotePort, RemoteUrl
| join kind=inner PS on DeviceId, ProcessId, ProcessCreationTime
| where NetworkTime >= ProcessCreationTime
and NetworkTime <= ProcessCreationTime + 5m
| project NetworkTime, DeviceName, AccountName, Parent,
ProcessCommandLine, ProcessId, ProcessCreationTime,
ActionType, RemoteIP, RemotePort, RemoteUrl
| order by NetworkTime descSPL — correlate endpoint and ProcessGuid
index=YOUR_ENDPOINT_INDEX sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" earliest=-1d latest=now (EventCode=1 OR EventCode=3)
(Image="*\\powershell.exe" OR Image="*\\pwsh.exe")
| where isnotnull(Computer) AND isnotnull(ProcessGuid)
| eventstats min(eval(if(EventCode=1,_time,null()))) AS ProcessStart
values(eval(if(EventCode=1,CommandLine,null()))) AS ProcessCommand
values(eval(if(EventCode=1,ParentImage,null()))) AS Parent
by Computer ProcessGuid
| where EventCode=3 AND isnotnull(ProcessStart)
AND _time >= ProcessStart AND _time <= ProcessStart+300
| table _time Computer ProcessGuid ProcessStart Parent ProcessCommand
User DestinationIp DestinationPort Initiated
| sort 0 - _timeThe KQL join uses the device, process ID and creation time; a PID alone can be reused. SPL groups by endpoint and Sysmon ProcessGuid. The five-minute window is a deliberate hunt boundary, not an adversary rule. Both queries can miss processes created before the selected search window, delayed connections, missing creation events, renamed executables and traffic from child processes. Expand the collection window when reviewing those cases.
Inspect Defender ActionType and Sysmon Initiated before describing an event as an outbound or successful connection. Destination fields and event semantics differ across platforms. These examples do not establish bytes transferred, stolen files, command-and-control, or whether a destination is new. Do not claim any of those from a matched row alone.
Turn the query results into an investigation
- Validate coverage. Confirm recent known activity is present on the devices in scope. Record retention, sensor configuration and ingestion delay.
- Preserve raw records. Keep event identifiers, timestamps, original fields and the exact query version. Store sensitive command lines under your evidence-handling policy.
- Reconstruct the process tree. Identify the parent, account, creation time and relevant children. Expand around the event when the selected window cuts off the beginning of the sequence.
- Enrich safely. Review destination context using approved DNS, proxy and threat-intelligence sources. Do not browse a suspicious destination from a normal workstation.
- Test competing explanations. Compare software deployment, signed maintenance tools, approved scripts and support activity against the actual observed records.
- Document and respond. Record a supported finding, a corroborated benign explanation or an unresolved visibility gap. Escalate unexplained activity to incident response; coordinate any isolation or account action through the authorized process.
Simulated case: a new destination after a rare parent
A low-prevalence parent starts PowerShell, and the correlated process contacts an unfamiliar service two minutes later. The observations justify review, but not a data-theft conclusion. If an approved asset-inventory job matches the process, destination and timing, document that explanation. If the executable identity and command remain unexplained, preserve the sequence and escalate with the evidence gaps.
Validate and tune before alerting
Use authorized benign records and synthetic event fixtures to check four cases: the same process with a connection at two minutes should match Hunt 2; a connection at six minutes should not; reuse of the same PID with a different creation time must not match the Defender join; and a network event without its process-creation record should be excluded and tracked as a coverage limitation. These are suggested validation cases, not claims that the queries were executed here.
Start with a bounded device cohort and short time range, then measure query cost. Splunk eventstats has memory limits; inspect search warnings and partial results before interpreting missing fields. Scope exceptions to verified workflows with an owner and expiry. Never suppress every execution by an administrator or all traffic to a familiar domain.
ATT&CK context and key takeaways
MITRE ATT&CK T1059.001 describes adversary use of PowerShell. Legitimate PowerShell use also matches the executable filters. Apply an adversary mapping only when the investigation supports it.
Takeaway: Prevalence helps prioritize; process correlation helps reconstruct; context supports the decision. Document what the query can see and what it cannot establish.
References and related reading
- Microsoft: DeviceNetworkEvents schema
- Splunk: stats reference
- Splunk: eventstats reference
- Part 1: PowerShell Threat Hunting — Two Practical KQL Examples
- Complete Knowledge Base
Reviewed September 20, 2026. Scenarios are simulated. Queries are educational starting points and require local validation.
EmoticonEmoticon