Agent Foskett Academy • Lesson 105 • Hunting Playbook Series

Hunting Playbook: PowerShell Abuse in Microsoft Defender XDR.

The process was signed by Microsoft.

The command window opened for only a moment. No malware name appeared. No ransomware note was created. Nothing obvious exploded on the endpoint.

But PowerShell had executed with unusual arguments, encoded content and a parent process that did not fit normal administration.

Agent Foskett did not investigate the file name alone.

He investigated the behaviour around it.

Agent Foskett Academy lesson hunting PowerShell abuse in Microsoft Defender XDR
Lesson overview

Learn how to hunt for PowerShell abuse by analysing process telemetry, command-line arguments, encoded commands, parent-child process relationships and follow-on activity in Microsoft Defender XDR.

Identify suspicious PowerShell execution
Review encoded commands and hidden windows
Correlate parent and child processes
Build a timeline of attacker execution

Why PowerShell abuse matters

PowerShell is a legitimate administration tool, which is exactly why attackers abuse it. The presence of PowerShell is not enough to prove compromise; defenders need to investigate how it was launched, what arguments were used and what happened next.
PowerShell is trustedPowerShell is built into Windows and commonly used by administrators, management agents and deployment tools.
Attackers use legitimate toolsAdversaries often use PowerShell for discovery, download, execution, persistence and defence evasion.
Behaviour matters more than the binaryThe investigation should focus on command-line arguments, parent processes, network activity and follow-on process creation.

The PowerShell abuse hunting workflow

Agent Foskett investigates suspicious PowerShell by moving from broad execution to command-line analysis, encoded content, process relationships, network activity and timeline reconstruction.
1. Find PowerShell executionStart with PowerShell process events across the investigation window and identify where it ran.
2. Inspect command-line argumentsLook for encoded commands, hidden windows, bypass flags, download cradles and suspicious script execution patterns.
3. Check parent processesDetermine what launched PowerShell. Office apps, browsers, script hosts and unusual parent processes deserve attention.
4. Review child processesLook for commands that launched additional tools, wrote files, changed persistence settings or spawned network utilities.
5. Correlate network activityPivot from the initiating process to outbound connections, remote URLs and suspicious IP addresses.
6. Build the execution timelineOrder process, network and file events so the investigation shows what happened before and after PowerShell executed.

Step 1 — Find recent PowerShell execution

step 1 find powershell execution.
step-1-find-powershell-execution.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
let TimeFrame = 7d;
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| project Timestamp,
          DeviceName,
          AccountName,
          FileName,
          ProcessCommandLine,
          InitiatingProcessFileName,
          InitiatingProcessCommandLine
| order by Timestamp desc

Step 2 — Hunt for suspicious PowerShell arguments

step 2 suspicious powershell arguments.
step-2-suspicious-powershell-arguments.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 TimeFrame = 7d;
let SuspiciousArguments = dynamic(["-enc", "-encodedcommand", "-nop", "-w hidden", "bypass", "downloadstring", "iex", "invoke-expression"]);
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (SuspiciousArguments)
| project Timestamp,
          DeviceName,
          AccountName,
          ProcessCommandLine,
          InitiatingProcessFileName,
          InitiatingProcessCommandLine
| order by Timestamp desc

Step 3 — Identify unusual parent processes

step 3 unusual parent processes.
step-3-unusual-parent-processes.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 TimeFrame = 14d;
let SuspiciousParents = dynamic(["winword.exe", "excel.exe", "outlook.exe", "chrome.exe", "msedge.exe", "wscript.exe", "cscript.exe", "mshta.exe"]);
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where InitiatingProcessFileName in~ (SuspiciousParents)
| project Timestamp,
          DeviceName,
          AccountName,
          InitiatingProcessFileName,
          InitiatingProcessCommandLine,
          ProcessCommandLine
| order by Timestamp desc

Step 4 — Summarise PowerShell activity by device

step 4 summarise by device.
step-4-summarise-by-device.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
let TimeFrame = 7d;
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| summarize PowerShellEvents = count(),
            Accounts = make_set(AccountName, 20),
            ParentProcesses = make_set(InitiatingProcessFileName, 20),
            ExampleCommands = make_set(ProcessCommandLine, 10),
            FirstSeen = min(Timestamp),
            LastSeen = max(Timestamp)
      by DeviceName
| order by PowerShellEvents desc

Step 5 — Correlate PowerShell with network activity

step 5 correlate network activity.
step-5-correlate-network-activity.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
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
let TimeFrame = 7d;
let PowerShellProcesses =
    DeviceProcessEvents
    | where Timestamp > ago(TimeFrame)
    | where FileName in~ ("powershell.exe", "pwsh.exe")
    | project DeviceId,
              DeviceName,
              ProcessTime = Timestamp,
              AccountName,
              ProcessCommandLine,
              ProcessId;
PowerShellProcesses
| join kind=inner (
    DeviceNetworkEvents
    | where Timestamp > ago(TimeFrame)
    | project DeviceId,
              NetworkTime = Timestamp,
              InitiatingProcessId,
              RemoteUrl,
              RemoteIP,
              RemotePort,
              ActionType
) on DeviceId
| where NetworkTime between (ProcessTime .. ProcessTime + 10m)
| project ProcessTime,
          NetworkTime,
          DeviceName,
          AccountName,
          ProcessCommandLine,
          RemoteUrl,
          RemoteIP,
          RemotePort,
          ActionType
| order by NetworkTime desc

Step 6 — Build a PowerShell investigation timeline

step 6 powershell timeline.
step-6-powershell-timeline.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
  28. 28
let TimeFrame = 7d;
let TargetDevice = "DEVICE-NAME";
union
(
    DeviceProcessEvents
    | where Timestamp > ago(TimeFrame)
    | where DeviceName =~ TargetDevice
    | where FileName in~ ("powershell.exe", "pwsh.exe") or InitiatingProcessFileName in~ ("powershell.exe", "pwsh.exe")
    | project Timestamp, EventType = "Process", DeviceName, AccountName, Evidence = ProcessCommandLine
),
(
    DeviceNetworkEvents
    | where Timestamp > ago(TimeFrame)
    | where DeviceName =~ TargetDevice
    | where InitiatingProcessFileName in~ ("powershell.exe", "pwsh.exe")
    | project Timestamp, EventType = "Network", DeviceName, AccountName = InitiatingProcessAccountName, Evidence = strcat(RemoteUrl, " ", RemoteIP)
),
(
    DeviceFileEvents
    | where Timestamp > ago(TimeFrame)
    | where DeviceName =~ TargetDevice
    | where InitiatingProcessFileName in~ ("powershell.exe", "pwsh.exe")
    | project Timestamp, EventType = "File", DeviceName, AccountName = InitiatingProcessAccountName, Evidence = FileName
)
| order by Timestamp asc

Common false positives

Administration scriptsIT teams frequently use PowerShell for legitimate automation, software deployment and configuration management.
Management agentsRMM, Intune, SCCM, backup and monitoring tools may launch PowerShell as part of normal operations.
Security toolingDefender, EDR, vulnerability scanners and response tools may generate PowerShell activity during investigation or remediation.

Real-world investigation

The alert looked minorThe initial signal was simply PowerShell execution with an encoded command and no obvious malware name.
The parent process was unusualPowerShell was launched by a browser process shortly after the user opened a suspicious link.
The command line matteredThe command included hidden execution and encoded content, indicating an attempt to avoid easy review.
Network activity followedWithin minutes, the same process chain attempted outbound connections to unfamiliar infrastructure.
Files were createdDefender telemetry showed file creation after PowerShell execution, giving investigators a clearer timeline.
The behaviour told the storyThe binary was legitimate, but the execution chain, arguments and follow-on actions were not.

Investigation checklist

Who launched PowerShell?Identify the account, device and initiating process responsible for execution.
What arguments were used?Review command-line flags, encoded content, hidden windows, bypass options and suspicious download patterns.
What launched it?Check whether PowerShell was started by an Office app, browser, script host, scheduled task or administrative tool.
What happened next?Look for child processes, file writes, registry changes, network connections and alerts after execution.
Is this normal for the device?Compare activity with normal administration, deployment tools and known management workflows.
Can the timeline explain the case?Build an ordered view of process, network and file events before making a response decision.

Related Agent Foskett Academy lessons

Lesson 104 — Hunting Playbook: OAuth AbuseInvestigate malicious consent and persistent application access.
Investigating DeviceProcessEventsUse process telemetry to investigate command execution and suspicious process chains.
Investigating DeviceNetworkEventsPivot from process execution into outbound network connections.
Building Device TimelinesCreate ordered endpoint timelines during Microsoft Defender XDR investigations.
Using Regular Expressions regex in KQLUse pattern matching to identify suspicious command-line values.
Building Reusable Hunting QueriesBuild repeatable PowerShell hunting queries for future investigations.

Coming next

Lesson 106 — Hunting Playbook: LOLBinsNext, Agent Foskett investigates Living Off the Land Binaries and how attackers abuse trusted Windows tools for execution, persistence and lateral movement.
Why this mattersPowerShell abuse prepares defenders to investigate broader living-off-the-land behaviour across Windows environments.

Final thought

PowerShell is not suspicious because it exists. It becomes suspicious when the behaviour around it does not fit the user, device or environment.
Agent Foskett mindsetDo not stop at the file name. Follow the parent process, the command line, the network activity and the timeline.
Hunting Playbook SeriesLesson 105 continues the practical investigation series by showing how defenders analyse attacker execution behaviour.
Develop IT. Protect IT.GEMXIT PTY LTD | GEMXIT UK LTD

Hunting PowerShell Abuse in Microsoft Defender XDR

Agent Foskett Academy Lesson 105 teaches defenders how to investigate PowerShell abuse, encoded commands, process telemetry and command-line activity in Microsoft Defender XDR.

Microsoft Defender XDR PowerShell abuse investigation playbook

This hunting playbook explains how to review PowerShell execution, suspicious arguments, parent-child process relationships, network activity and endpoint timelines during Windows compromise investigations.

Learn to hunt malicious PowerShell with KQL

Defenders can use KQL, DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents and Microsoft Defender XDR telemetry to detect suspicious PowerShell execution and post-compromise attacker behaviour.