Create custom detection rules using KQL, configure automated investigation and response, set up suppression rules, and validate detection accuracy in Microsoft Defender XDR.
In this hands-on lab you will author production-grade custom detection rules in Microsoft Defender XDR using Kusto Query Language (KQL). You will go beyond the built-in detection catalog by writing tailored queries that target suspicious PowerShell activity and lateral movement techniques. Once the queries are validated, you will promote them to scheduled custom detection rules, wire them into Automated Investigation and Response (AIR), define auto-response actions, configure suppression rules to tame false positives, and perform end-to-end validation to confirm that alerts fire and automated remediation triggers correctly.
Contoso Ltd, a financial services organization with 5,000 endpoints, has observed a sharp increase in commodity malware that leverages encoded PowerShell commands and RMM tools for initial access, followed by lateral movement via PsExec and WMI. The SOC team needs custom detections that fire within minutes: not hours: and trigger automated containment to limit blast radius during off-hours when analyst coverage drops to a single L1 analyst. By the end of this lab you will have built exactly that pipeline: detection → alert → automated response → suppression of known-good patterns.
DeviceEvents, DeviceProcessEvents, and DeviceNetworkEvents tablesBuilt-in detections cover a vast threat landscape, but every organization has unique processes, tools, and baseline behaviors that commodity detections cannot account for. Custom detection rules let you close visibility gaps that attackers actively exploit. When paired with automated response, these rules transform your security posture from reactive: waiting for an analyst to triage: to proactive, containing threats in seconds. Suppression rules ensure that automation does not overwhelm your SOC with false positives, keeping signal-to-noise ratios manageable. Mastering this workflow is essential for any security team operating at enterprise scale.
Before writing detection rules you need a solid understanding of the data available to you. Microsoft Defender XDR exposes dozens of tables through Advanced Hunting. For endpoint-focused custom detections the three most important tables are DeviceProcessEvents, DeviceEvents, and DeviceNetworkEvents. Each table captures different telemetry and together they provide a full picture of what happened on a device.
Timestamp, DeviceName, FileName, ProcessCommandLine, InitiatingProcessFileName, AccountNameActionType column: this table captures a wide variety of sensor eventsRemoteUrl, RemoteIP, RemotePortStart by exploring recent process creation events to familiarize yourself with the data shape:
// BASELINE: Explore recent process creation events on your endpoints
// Purpose: Familiarize yourself with the DeviceProcessEvents table structure
// before writing detections. This is the most important table for detecting
// malicious execution on endpoints.
// Key columns:
// DeviceName – endpoint hostname
// FileName – the process that was launched (e.g., powershell.exe)
// ProcessCommandLine – full command line including arguments (key for detection)
// InitiatingProcessFileName – WHAT launched the process (parent process)
// AccountName – the user account that ran the process
// The relationship between InitiatingProcessFileName and FileName reveals
// the process chain (e.g., outlook.exe → powershell.exe = macro attack)
DeviceProcessEvents
| where Timestamp > ago(1h)
| project Timestamp, DeviceName, FileName, ProcessCommandLine,
InitiatingProcessFileName, AccountName
| take 50Examine the most common ActionType values to understand what the sensor reports:
// BASELINE: Top ActionTypes reported by the MDE sensor in last 24h
// Purpose: Understand what types of events the DeviceEvents table captures.
// DeviceEvents is a catch-all table for sensor events that don’t fit into
// the specialized tables (ProcessEvents, FileEvents, etc.).
// Common ActionTypes you’ll see:
// AntivirusDetection – AV engine found a threat
// ExploitGuardNetworkProtect – network protection blocked a connection
// CreateRemoteThreadApiCall – process injection (T1055)
// PowerShellCommand – PowerShell script block logging events
// UsbDriveMounted – removable media connected
// Output: Count column shows how frequently each type fires.
// Use this to understand your environment’s noise baseline.
DeviceEvents
| where Timestamp > ago(24h)
| summarize Count = count() by ActionType
| sort by Count desc
| take 20Get a quick overview of outbound network connections and the processes initiating them:
// BASELINE: Which processes are making the most outbound connections?
// Purpose: Understand network behavior on your endpoints. The
// DeviceNetworkEvents table captures all TCP/UDP connections.
// Filters:
// ActionType == "ConnectionSuccess" – only successful connections
// (filters out blocked/failed attempts which are higher volume)
// Output: InitiatingProcessFileName – the process that opened the connection
// High ConnectionCount from unexpected processes (e.g., notepad.exe,
// rundll32.exe opening network connections) is suspicious.
// Normal: browsers, outlook.exe, teams.exe will have high counts.
// Suspicious: cmd.exe, powershell.exe, certutil.exe with network activity.
DeviceNetworkEvents
| where Timestamp > ago(1h)
| where ActionType == "ConnectionSuccess"
| summarize ConnectionCount = count() by InitiatingProcessFileName
| sort by ConnectionCount desc
| take 15getschema operator (e.g., DeviceProcessEvents | getschema) to list every column with its data type. This is invaluable when building queries programmatically or when you can't remember the exact column name.PowerShell is the most abused living-off-the-land binary (LOLBin) in enterprise environments. Attackers routinely use encoded commands (-EncodedCommand), download cradles (IEX(New-Object Net.WebClient)), and obfuscation techniques (string concatenation, backtick insertion) to evade static analysis. In this step you will write detection queries that catch these patterns.
This query identifies PowerShell processes launched with base64-encoded commands, a hallmark of malware stagers and C2 frameworks:
// DETECTION 2a: Encoded PowerShell Commands
// MITRE ATT&CK: T1059.001 (Command and Scripting Interpreter: PowerShell)
// Why: Base64-encoded commands hide malicious payloads from static analysis.
// Legitimate automation rarely uses -EncodedCommand; attackers almost always do.
//
// Detection flags:
// -EncodedCommand / -enc / -ec / -e – PowerShell’s built-in Base64 decode flag
// FromBase64String – .NET method used in script to decode payloads inline
//
// Exclusions: svc_sccm and svc_intune are examples of known service accounts
// that legitimately run encoded commands. Replace with YOUR environment’s
// service account names.
//
// Key output columns:
// InitiatingProcessFileName – what launched PowerShell tells you the attack vector:
// cmd.exe = manual execution; wmiprvse.exe = WMI remote exec;
// outlook.exe/winword.exe = macro-based attack
// ReportId – unique event ID required for custom detection rule entity mapping
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (
"-EncodedCommand",
"-enc ",
"-ec ",
"-e ",
"FromBase64String"
)
// Exclude known legitimate automation accounts
| where AccountName !in~ ("svc_sccm", "svc_intune")
| project Timestamp, DeviceName, AccountName,
ProcessCommandLine, InitiatingProcessFileName,
InitiatingProcessCommandLine, ReportId
| sort by Timestamp descDownload cradles fetch and immediately execute remote payloads. The following query catches the most common variants:
// DETECTION 2b: PowerShell Download Cradles
// MITRE ATT&CK: T1059.001 (PowerShell) + T1105 (Ingress Tool Transfer)
// Why: Download cradles fetch a remote script and execute it immediately
// in memory, leaving minimal disk artifacts. This is the #1 technique
// for malware stagers and C2 framework initial access.
//
// Two-stage filter:
// 1. First has_any: checks for download methods
// Net.WebClient/DownloadString/DownloadFile = .NET web download
// Invoke-WebRequest/iwr/wget/curl = PowerShell cmdlet aliases
// Start-BitsTransfer = Windows BITS service download
// Invoke-RestMethod/irm = REST API download
// 2. Second has_any: checks for immediate execution
// IEX/Invoke-Expression/iex( = execute downloaded content in memory
// | iex = pipeline execution (download | invoke pattern)
//
// The combination of download + execute is what makes this dangerous.
// Download alone might be legitimate; execute alone might be legitimate;
// both together in PowerShell is almost always malicious.
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (
"Net.WebClient",
"DownloadString",
"DownloadFile",
"Invoke-WebRequest",
"iwr ",
"wget ",
"curl ",
"Start-BitsTransfer",
"Invoke-RestMethod",
"irm "
)
| where ProcessCommandLine has_any (
"IEX",
"Invoke-Expression",
"iex(",
"| iex"
)
| project Timestamp, DeviceName, AccountName,
ProcessCommandLine, InitiatingProcessFileName,
InitiatingProcessCommandLine, ReportIdAttackers insert backticks, carets, or excessive string concatenation to evade signature-based detections. This query uses a heuristic based on the ratio of special characters to command length:
// DETECTION 2c: Obfuscated PowerShell via Special Character Density
// MITRE ATT&CK: T1027 (Obfuscated Files or Information)
// Why: Attackers insert backticks (`), carets (^), and string concatenation (+)
// to break apart known-bad keywords and evade signature-based detection.
// Example: I`n`v`o`k`e`-`E`x`p`r`e`s`s`i`o`n evades matching "Invoke-Expression"
//
// Heuristic approach:
// Instead of matching specific obfuscated patterns (infinite variations),
// this query measures the RATIO of special characters to total command length.
// Normal PowerShell commands have < 1% special characters.
// Obfuscated commands typically have > 5% (the 0.05 threshold).
//
// CmdLen > 100 filter: Short commands don’t need obfuscation detection;
// the threshold focuses on longer payloads where obfuscation is meaningful.
//
// Output: SpecialTotal shows how many obfuscation characters were found.
// Values > 20 with CmdLen > 200 are very likely malicious obfuscation.
// False positives: Some legitimate scripts use backticks for line continuation.
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| extend CmdLen = strlen(ProcessCommandLine)
| extend TickCount = countof(ProcessCommandLine, "`")
| extend CaretCount = countof(ProcessCommandLine, "^")
| extend PlusCount = countof(ProcessCommandLine, "+")
| extend SpecialTotal = TickCount + CaretCount + PlusCount
| where CmdLen > 100
and (SpecialTotal * 1.0 / CmdLen) > 0.05
| project Timestamp, DeviceName, AccountName,
ProcessCommandLine, CmdLen, SpecialTotal,
InitiatingProcessFileName, ReportIdwhere exclusions to suppress known-good activity in your environmentago(7d)) to verify result volume is manageableInitiatingProcessFileName column. If PowerShell is launched by cmd.exe or explorer.exe, it is likely user-initiated. If launched by wmiprvse.exe or svchost.exe, it may indicate an automated or remote execution: exactly what you want to detect.Lateral movement is a critical stage in the attack kill chain. Adversaries use legitimate administrative tools: PsExec, WMI, and SMB: to spread across the network. Writing detections for these techniques requires distinguishing between legitimate admin activity and attacker behavior, typically by examining the parent process, timing, and target scope.
PsExec creates a service on the remote machine. Detect both the original SysInternals binary and renamed copies:
// DETECTION 3a: PsExec Execution (original or renamed)
// MITRE ATT&CK: T1569.002 (System Services: Service Execution)
// T1021.002 (Remote Services: SMB/Windows Admin Shares)
// Why: PsExec is the classic admin tool for remote command execution.
// Attackers use it for lateral movement by creating a temporary service
// on the target machine. They often rename psexec.exe to evade filename-based
// detections, so this query also checks the original file metadata.
//
// Detection logic:
// 1. Direct name match: psexec.exe or psexec64.exe
// 2. Service name match: "psexesvc" in command line (the service PsExec creates)
// 3. Renamed copy detection: FileName is different but
// ProcessVersionInfoOriginalFileName still shows "psexec.exe"
// (file metadata is harder to forge than renaming the file)
//
// Output: Check AccountName – is this an expected admin? Check
// InitiatingProcessFileName – was PsExec launched from cmd.exe (manual)
// or from another process (automated attack chain)?
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName =~ "psexec.exe"
or FileName =~ "psexec64.exe"
or ProcessCommandLine has "psexesvc"
or (
// Detect renamed PsExec by original file name metadata
FileName != "psexec.exe"
and ProcessVersionInfoOriginalFileName =~ "psexec.exe"
)
| project Timestamp, DeviceName, AccountName,
FileName, ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine, ReportId
| sort by Timestamp descWMI remote process creation uses wmic.exe or the WMI provider (wmiprvse.exe) to spawn processes on remote machines:
// DETECTION 3b: WMI Remote Process Creation
// MITRE ATT&CK: T1047 (Windows Management Instrumentation)
// Why: WMI is a built-in Windows framework for remote administration.
// Attackers abuse it for fileless lateral movement because:
// - It’s signed by Microsoft and present on every Windows machine
// - It doesn’t require additional tools (no PsExec needed)
// - Processes spawned by wmiprvse.exe can evade parent-process-based rules
//
// Two detection patterns:
// 1. WMIC with /node: targeting a remote host + process call create
// = explicit remote process creation via the WMIC command-line tool
// 2. Processes spawned by wmiprvse.exe (the WMI provider service)
// = if WMI spawns PowerShell, cmd, mshta, etc., it’s likely remote execution
// because normal users don’t interact with WMI directly for these tools
//
// False positives: SCCM, Intune, monitoring agents use WMI heavily.
// Filter by AccountName to exclude known management service accounts.
DeviceProcessEvents
| where Timestamp > ago(1h)
| where (
// WMIC with /node targeting a remote host
FileName =~ "wmic.exe"
and ProcessCommandLine has "/node:"
and ProcessCommandLine has "process"
and ProcessCommandLine has "call"
and ProcessCommandLine has "create"
)
or (
// Processes spawned by the WMI provider service
InitiatingProcessFileName =~ "wmiprvse.exe"
and FileName in~ ("powershell.exe", "cmd.exe", "mshta.exe",
"cscript.exe", "wscript.exe", "rundll32.exe")
)
| project Timestamp, DeviceName, AccountName,
FileName, ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine, ReportIdDetect processes that write executables or scripts to remote admin shares (C$, ADMIN$), a common lateral movement pattern:
// DETECTION 3c: Suspicious SMB File Writes to Admin Shares
// MITRE ATT&CK: T1021.002 (Remote Services: SMB/Windows Admin Shares)
// T1570 (Lateral Tool Transfer)
// Why: During lateral movement, attackers copy malicious executables or
// scripts to remote admin shares (C$, ADMIN$, IPC$) to stage them
// for execution on the target machine. This is often paired with
// PsExec or scheduled task creation.
//
// Detection logic:
// ActionType == "FileCreated" – a new file was written
// ShareName in (ADMIN$, C$, IPC$) – administrative shares only
// File extension filter – executables and scripts (not docs or data)
//
// Output:
// FolderPath – where the file was written (ADMIN$ = Windows directory)
// InitiatingProcessFileName – what process wrote the file
// InitiatingProcessAccountName – the user/account performing the write
//
// False positives: SCCM, PDQ Deploy, and WSUS write to admin shares.
// Exclude by InitiatingProcessAccountName or InitiatingProcessFileName.
DeviceFileEvents
| where Timestamp > ago(1h)
| where ActionType == "FileCreated"
| where ShareName in~ ("ADMIN$", "C$", "IPC$")
| where FileName endswith ".exe"
or FileName endswith ".dll"
or FileName endswith ".ps1"
or FileName endswith ".bat"
or FileName endswith ".vbs"
| project Timestamp, DeviceName, AccountName,
FileName, FolderPath, ShareName,
InitiatingProcessFileName,
InitiatingProcessAccountName, ReportIdAccountName or DeviceNamewmiprvse.exe spawns interpreters, not when it runs benign WMI queriesNow that your KQL queries are validated, you will promote one to a scheduled custom detection rule. Custom detection rules run on a schedule, automatically generating alerts and populating the incident queue. You will configure the rule name, frequency, entity mapping, and alert enrichment.
Custom detection rules require specific column names for entity mapping. Modify your query to include the required columns:
// PRODUCTION-READY: Encoded PowerShell Detection Rule Query
// MITRE ATT&CK: T1059.001 (Command and Scripting Interpreter: PowerShell)
// Purpose: This is the final query optimized for promotion to a custom
// detection rule. It includes entity mapping columns required by Defender XDR.
//
// Entity mapping columns (configure in portal when creating rule):
// DeviceId + DeviceName → Device entity (enables device page, isolation)
// AccountObjectId + AccountName + AccountDomain → User entity
// ReportId → unique event identifier for deduplication
// AlertTitle → dynamic alert name that includes device and user context
//
// Scheduling: Set frequency to 1 hour, lookback to 1 hour (matches ago(1h)).
// The query runs every hour and scans the last hour of data.
// Service account exclusions: Replace svc_sccm/svc_intune with YOUR accounts.
DeviceProcessEvents
| where Timestamp > ago(1h)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (
"-EncodedCommand", "-enc ", "-ec ", "-e ",
"FromBase64String"
)
| where AccountName !in~ ("svc_sccm", "svc_intune")
// Entity mapping columns (required for custom detection)
| extend AlertTitle = strcat("Encoded PowerShell on ", DeviceName,
" by ", AccountName)
| project Timestamp, ReportId,
// Device entity
DeviceId, DeviceName,
// Account entity
AccountObjectId, AccountName, AccountDomain,
// Process entity
FileName, ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
AlertTitleEncoded PowerShell Execution Detectedago() value in your query)DeviceId and DeviceNameAccountObjectId, AccountName, AccountDomainReportId in your query projection. Defender XDR uses ReportId as the unique event identifier for alert correlation and deduplication. Without it, you may see duplicate alerts for the same event.Automated Investigation and Response (AIR) allows Defender XDR to automatically investigate alerts, determine the scope of a threat, and take remediation actions. Before wiring your custom detection rules to auto-response actions, you need to ensure AIR is properly configured with the right automation levels and device groups.
Defender XDR offers four automation levels:
High-Value ServersDevice tag equals HVAStandard WorkstationsOS Platform equals Windows10+ and Device tag does not equal HVAHVA, PAW, TestLab to segment your fleet.With AIR configured, you can now attach automated response actions directly to your custom detection rules. These actions execute immediately when the rule fires, providing near-real-time containment. Defender XDR supports four primary auto-response actions on devices: isolate, quarantine file, block URL/IP, and collect investigation package.
DeviceId entity from your queryInitiatingProcessSHA1 or add SHA1 to your query projectionIf your detection query joins with DeviceNetworkEvents and surfaces a malicious URL or IP, you can create a block indicator automatically:
Even well-tuned detection rules will occasionally fire on legitimate activity. Suppression rules let you silence specific alert instances without disabling the entire detection rule. This keeps your SOC focused on genuine threats while preserving your detection coverage for scenarios you haven't excluded.
svc_deploy for a deployment service account)Encoded PowerShell Execution Detected)AccountName equals svc_backup ANDDeviceName starts with SRV-BACKUPThe final step is end-to-end validation. You will generate synthetic test events on a test endpoint, wait for the custom detection rule to fire, verify the alert appears in the incident queue, and confirm that automated response actions were triggered. Then you will tune the rule based on the results.
On your test endpoint (not a production device), run the following PowerShell commands to simulate encoded command execution:
# ============================================================
# SIMULATED TEST EVENTS: Encoded PowerShell
# Purpose: Generate endpoint telemetry for detection rule validation.
# IMPORTANT: Run these ONLY on a designated test endpoint, never production.
# These commands are benign but generate the same MDE telemetry as real attacks.
# ============================================================
# Test 1: Encoded Command
# Creates a Base64-encoded "Write-Host" command and runs it via -EncodedCommand.
# The MDE sensor will log this as a powershell.exe process with -EncodedCommand
# in ProcessCommandLine, which should trigger Detection 2a.
$command = 'Write-Host "Detection test. encoded command"'
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encoded = [Convert]::ToBase64String($bytes)
powershell.exe -EncodedCommand $encoded
# Test 2: Download Cradle
# Attempts to download and execute a script from localhost (will fail safely).
# The MDE sensor logs the full command line including IEX + DownloadString,
# which should trigger Detection 2b.
powershell.exe -Command "IEX(New-Object Net.WebClient).DownloadString('http://127.0.0.1/test.ps1')"
# Test 3: Obfuscated Command
# Uses backtick insertion to break apart known keywords.
# The MDE sensor captures the full command line with backticks intact,
# which should trigger Detection 2c (special character density check).
powershell.exe -Command "I`E`X (N`e`w-O`b`j`e`c`t N`e`t.W`e`b`C`l`i`e`n`t).D`o`w`n`l`o`a`d`S`t`r`i`n`g('http://127.0.0.1/test')"Simulate PsExec and WMI activity (use only against your test lab machines):
# ============================================================
# SIMULATED TEST EVENTS: Lateral Movement (PsExec & WMI)
# Purpose: Generate endpoint telemetry for lateral movement detection rules.
# IMPORTANT: Run ONLY on test lab machines. PsExec requires the SysInternals
# binary to be downloaded first. WMI commands execute locally as stand-ins.
# ============================================================
# Test 1: PsExec local execution
# -accepteula bypasses the EULA prompt; -s runs as SYSTEM account.
# The MDE sensor logs psexec.exe process creation + the psexesvc service creation,
# which should trigger Detection 3a.
.\PsExec.exe -accepteula -s cmd.exe /c "echo Detection Test"
# Test 2: WMI local process creation
# Creates a cmd.exe process via WMI – the MDE sensor will see wmiprvse.exe
# as the parent process of cmd.exe, which triggers Detection 3b.
wmic process call create "cmd.exe /c echo WMI-Test"
# Test 3: WMI remote targeting (using localhost as safe stand-in)
# The /node:127.0.0.1 parameter simulates targeting a remote host.
# In production, this would target another machine’s IP or hostname.
# The MDE sensor logs the wmic.exe command line with /node: + process call create.
wmic /node:127.0.0.1 process call create "cmd.exe /c echo WMI-Remote-Test"has_any conditions// ESTIMATE DETECTION RULE ALERT VOLUME (30-day projection)
// Purpose: Before deploying a detection rule to production, run this query
// to estimate how many alerts it would generate per day over a 30-day period.
// This helps with SOC capacity planning and false positive assessment.
//
// Output columns (per day):
// TotalEvents – number of times the detection would fire
// UniqueDevices – how many distinct endpoints triggered it
// UniqueUsers – how many distinct user accounts triggered it
//
// Target thresholds:
// TotalEvents < 10/day = manageable for manual triage
// TotalEvents 10-50/day = consider adding exclusions or raising thresholds
// TotalEvents > 50/day = too noisy for production; needs significant tuning
//
// If UniqueDevices or UniqueUsers is very low but TotalEvents is high,
// a single device/user is generating most alerts → likely a false positive
// candidate for a suppression rule.
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName in~ ("powershell.exe", "pwsh.exe")
| where ProcessCommandLine has_any (
"-EncodedCommand", "-enc ", "-ec ", "-e ",
"FromBase64String"
)
| where AccountName !in~ ("svc_sccm", "svc_intune")
| summarize
TotalEvents = count(),
UniqueDevices = dcount(DeviceName),
UniqueUsers = dcount(AccountName)
by bin(Timestamp, 1d)
| sort by Timestamp ascDeviceProcessEvents, DeviceEvents, and DeviceNetworkEvents| Resource | Description |
|---|---|
| Custom detections overview: Microsoft Defender XDR | Overview of custom detection capabilities in the Defender XDR portal |
| Create and manage custom detection rules | Build automated detection rules from advanced hunting queries |
| Advanced hunting overview: Microsoft Defender XDR | Proactively search for threats using KQL across workloads |
| Advanced hunting schema reference | Complete reference for tables and columns in the hunting schema |
| Automated investigation and response in Microsoft Defender for Endpoint | Configure automated investigation and remediation for endpoint alerts |
| Automation levels in automated investigation and remediation | Understand and configure automation levels for incident response |
| Manage alert suppression rules | Create rules to suppress known benign or expected alerts |
| Respond to your first incident in Microsoft Defender XDR | Step-by-step guide to investigating your first XDR incident |