Advanced Multi Stage Windows Enumeration Post Exploitation Detector
Query
// ============================================================================
// Windows Discovery & Reconnaissance Detection — OPTIMIZED v2
// Detects post-exploitation enumeration with minimal false positives
// ============================================================================
let timeframe = 4h;
let threshold = 5; // Anzahl unterschiedlicher Einzelbefehle pro Zeitfenster
// --- ALLOWLISTS ---
let excludedAccounts = dynamic([
"SYSTEM", "NETWORK SERVICE", "LOCAL SERVICE", "SccmAdmin", "BackupUser"
]);
let excludedParentImages = dynamic([
"sccm-agent.exe", "monitoring-agent.exe", "taniumclient.exe", "qualysagent.exe",
"nessus.exe", "ansible-windows.exe", "salt-minion.exe", "msiexec.exe"
]);
// --- PATTERN DEFINITIONS ---
let suspiciousPatterns = dynamic([
// User & Group Discovery
"whoami /all", "whoami /groups", "net user", "net localgroup", "net group",
"quser", "query user", "cmdkey /list", "net accounts",
// Network & Domain Discovery
"ipconfig /all", "route print", "netstat -ano", "arp -a", "net view",
"nltest /domain_trusts", "nltest /dclist:", "nslookup", "dnscmd", "net share",
"adsiedit.msc", "ldp.exe", "setspn -q", "setspn -t",
// System & Config Discovery
"systeminfo", "hostname", "ver", "wmic qfe", "reg query", "sc query",
"tasklist", "driverquery", "gpresult /r", "auditpol /get",
// Privilege Escalation / Credential Hunting
"mimikatz", "procdump", "savedcredentials", "vaultcmd", "lazagne",
"PowerUp", "Get-GPPPassword", "findstr /s /i password", "dir /s *pass*",
// Cloud / Azure Discovery
"169.254.169.254", "az account get-access-token", "Get-AzADUser",
// Active Directory Recon
"Get-NetUser", "Get-NetComputer", "Get-DomainController", "Get-NetGroup",
"Get-ADUser", "Get-ADComputer", "Get-ADGroupMember"
]);
// --- MAIN QUERY ---
DeviceProcessEvents
| where TimeGenerated > ago(timeframe)
| where ProcessCommandLine has_any (suspiciousPatterns)
// ---- FP FILTER ----
| where AccountName !in (excludedAccounts)
| where InitiatingProcessFileName !in~ (excludedParentImages)
| where not(
InitiatingProcessFileName has_any ("windowsupdate.exe", "wusa.exe", "trustedinstaller.exe")
and ProcessCommandLine has_any ("update", "install")
)
// ---- Kategorisierung ----
| extend NormalizedCommand = case(
// High Risk
ProcessCommandLine has_any (
"mimikatz", "procdump", "vaultcmd", "Get-GPPPassword",
"savedcredentials", "lazagne", "PowerUp",
"findstr /s /i password", "dir /s *pass*"
), "Credential_Hunting",
ProcessCommandLine has_any (
"nltest", "setspn", "adsiedit", "ldp.exe",
"Get-DomainController", "Get-NetUser", "Get-NetComputer",
"Get-NetGroup", "Get-ADUser", "Get-ADComputer",
"Get-ADGroupMember", "net group /domain"
), "AD_Domain_Recon",
ProcessCommandLine has "169.254.169.254"
or ProcessCommandLine has "az account"
or ProcessCommandLine has "Get-AzADUser", "Cloud_Metadata_Discovery",
// Medium Risk
ProcessCommandLine has_any (
"net user", "net localgroup", "whoami /all",
"whoami /groups", "cmdkey", "quser", "query user", "net accounts"
), "User_Group_Enum",
ProcessCommandLine has_any (
"net share", "net view", "route print",
"netstat", "arp -a", "nslookup", "dnscmd"
), "Network_Share_Discovery",
ProcessCommandLine has_any (
"gpresult", "auditpol", "wmic qfe", "reg query", "sc query", "driverquery"
), "Policy_Security_Enum",
// Low Risk
ProcessCommandLine has_any (
"systeminfo", "hostname", "tasklist", "ver", "ipconfig"
), "System_Info_Discovery",
"General_Discovery"
)
// --- Aggregation ---
| summarize
UniqueCommandCount = dcount(ProcessCommandLine), // Anzahl unterschiedlicher Einzelbefehle
DetectedCategories = make_set(NormalizedCommand),
DetailedCommands = make_set(ProcessCommandLine, 20),
CommandCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
ReportId = take_any(ReportId) // Single ReportId für Custom Detection
by DeviceId, DeviceName, AccountName
// Filter auf Schwellenwert
| where UniqueCommandCount >= threshold
// --- Risk Scoring ---
| extend RiskScore =
iif(set_has_element(DetectedCategories, "Credential_Hunting"), 4, 0)
+ iif(set_has_element(DetectedCategories, "AD_Domain_Recon"), 3, 0)
+ iif(set_has_element(DetectedCategories, "Cloud_Metadata_Discovery"), 3, 0)
+ iif(set_has_element(DetectedCategories, "User_Group_Enum"), 2, 0)
+ iif(set_has_element(DetectedCategories, "Network_Share_Discovery"), 2, 0)
+ iif(set_has_element(DetectedCategories, "Policy_Security_Enum"), 2, 0)
+ iif(set_has_element(DetectedCategories, "System_Info_Discovery"), 1, 0)
| extend ActivityDurationMinutes = datetime_diff('minute', LastSeen, FirstSeen)
// --- Severity Classification ---
| extend Severity = case(
RiskScore >= 10 and ActivityDurationMinutes between (1 .. 15), "Critical",
RiskScore >= 7, "High",
RiskScore >= 4, "Medium",
"Low"
)
| where Severity in ("Critical", "High", "Medium")
// --- Output ---
| project
Timestamp = FirstSeen,
DeviceName,
AccountName,
Severity,
RiskScore,
UniqueCommandCount,
CommandCount,
ActivityDurationMinutes,
DetectedCategories,
DetailedCommands,
DeviceId,
ReportId
| order by RiskScore desc
// ============================================================================
// Windows Discovery & Reconnaissance Detection — OPTIMIZED v2
// Detects post-exploitation enumeration with minimal false positives
// ============================================================================
let timeframe = 4h;
let threshold = 5; // Anzahl unterschiedlicher Einzelbefehle pro Zeitfenster
// --- ALLOWLISTS ---
let excludedAccounts = dynamic([
"SYSTEM", "NETWORK SERVICE", "LOCAL SERVICE", "SccmAdmin", "BackupUser"
]);
let excludedParentImages = dynamic([
"sccm-agent.exe", "monitoring-agent.exe", "taniumclient.exe", "qualysagent.exe",
"nessus.exe", "ansible-windows.exe", "salt-minion.exe", "msiexec.exe"
]);
// --- PATTERN DEFINITIONS ---
let suspiciousPatterns = dynamic([
// User & Group Discovery
"whoami /all", "whoami /groups", "net user", "net localgroup", "net group",
"quser", "query user", "cmdkey /list", "net accounts",
// Network & Domain Discovery
"ipconfig /all", "route print", "netstat -ano", "arp -a", "net view",
"nltest /domain_trusts", "nltest /dclist:", "nslookup", "dnscmd", "net share",
"adsiedit.msc", "ldp.exe", "setspn -q", "setspn -t",
// System & Config Discovery
"systeminfo", "hostname", "ver", "wmic qfe", "reg query", "sc query",
"tasklist", "driverquery", "gpresult /r", "auditpol /get",
// Privilege Escalation / Credential Hunting
"mimikatz", "procdump", "savedcredentials", "vaultcmd", "lazagne",
"PowerUp", "Get-GPPPassword", "findstr /s /i password", "dir /s *pass*",
// Cloud / Azure Discovery
"169.254.169.254", "az account get-access-token", "Get-AzADUser",
// Active Directory Recon
"Get-NetUser", "Get-NetComputer", "Get-DomainController", "Get-NetGroup",
"Get-ADUser", "Get-ADComputer", "Get-ADGroupMember"
]);
// --- MAIN QUERY ---
DeviceProcessEvents
| where TimeGenerated > ago(timeframe)
| where ProcessCommandLine has_any (suspiciousPatterns)
// ---- FP FILTER ----
| where AccountName !in (excludedAccounts)
| where InitiatingProcessFileName !in~ (excludedParentImages)
| where not(
InitiatingProcessFileName has_any ("windowsupdate.exe", "wusa.exe", "trustedinstaller.exe")
and ProcessCommandLine has_any ("update", "install")
)
// ---- Kategorisierung ----
| extend NormalizedCommand = case(
// High Risk
ProcessCommandLine has_any (
"mimikatz", "procdump", "vaultcmd", "Get-GPPPassword",
"savedcredentials", "lazagne", "PowerUp",
"findstr /s /i password", "dir /s *pass*"
), "Credential_Hunting",
ProcessCommandLine has_any (
"nltest", "setspn", "adsiedit", "ldp.exe",
"Get-DomainController", "Get-NetUser", "Get-NetComputer",
"Get-NetGroup", "Get-ADUser", "Get-ADComputer",
"Get-ADGroupMember", "net group /domain"
), "AD_Domain_Recon",
ProcessCommandLine has "169.254.169.254"
or ProcessCommandLine has "az account"
or ProcessCommandLine has "Get-AzADUser", "Cloud_Metadata_Discovery",
// Medium Risk
ProcessCommandLine has_any (
"net user", "net localgroup", "whoami /all",
"whoami /groups", "cmdkey", "quser", "query user", "net accounts"
), "User_Group_Enum",
ProcessCommandLine has_any (
"net share", "net view", "route print",
"netstat", "arp -a", "nslookup", "dnscmd"
), "Network_Share_Discovery",
ProcessCommandLine has_any (
"gpresult", "auditpol", "wmic qfe", "reg query", "sc query", "driverquery"
), "Policy_Security_Enum",
// Low Risk
ProcessCommandLine has_any (
"systeminfo", "hostname", "tasklist", "ver", "ipconfig"
), "System_Info_Discovery",
"General_Discovery"
)
// --- Aggregation ---
| summarize
UniqueCommandCount = dcount(ProcessCommandLine), // Anzahl unterschiedlicher Einzelbefehle
DetectedCategories = make_set(NormalizedCommand),
DetailedCommands = make_set(ProcessCommandLine, 20),
CommandCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
ReportId = take_any(ReportId) // Single ReportId für Custom Detection
by DeviceId, DeviceName, AccountName
// Filter auf Schwellenwert
| where UniqueCommandCount >= threshold
// --- Risk Scoring ---
| extend RiskScore =
iif(set_has_element(DetectedCategories, "Credential_Hunting"), 4, 0)
+ iif(set_has_element(DetectedCategories, "AD_Domain_Recon"), 3, 0)
+ iif(set_has_element(DetectedCategories, "Cloud_Metadata_Discovery"), 3, 0)
+ iif(set_has_element(DetectedCategories, "User_Group_Enum"), 2, 0)
+ iif(set_has_element(DetectedCategories, "Network_Share_Discovery"), 2, 0)
+ iif(set_has_element(DetectedCategories, "Policy_Security_Enum"), 2, 0)
+ iif(set_has_element(DetectedCategories, "System_Info_Discovery"), 1, 0)
| extend ActivityDurationMinutes = datetime_diff('minute', LastSeen, FirstSeen)
// --- Severity Classification ---
| extend Severity = case(
RiskScore >= 10 and ActivityDurationMinutes between (1 .. 15), "Critical",
RiskScore >= 7, "High",
RiskScore >= 4, "Medium",
"Low"
)
| where Severity in ("Critical", "High", "Medium")
// --- Output ---
| project
Timestamp = FirstSeen,
DeviceName,
AccountName,
Severity,
RiskScore,
UniqueCommandCount,
CommandCount,
ActivityDurationMinutes,
DetectedCategories,
DetailedCommands,
DeviceId,
ReportId
| order by RiskScore desc
| where RiskScore >=7About this query
Explanation
This KQL query is designed to detect suspicious activities on Windows systems that may indicate post-exploitation behavior, such as unauthorized access or data gathering by an attacker. Here's a simplified breakdown of what the query does:
-
Timeframe and Threshold: It looks at events from the last 4 hours and requires at least 5 different suspicious commands to trigger an alert.
-
Allowlists: It excludes certain known accounts and processes (like system services and common administrative tools) to reduce false positives.
-
Suspicious Patterns: The query identifies a set of command patterns that are often used in malicious activities, such as:
- Discovering user and group information.
- Exploring network and domain configurations.
- Gathering system information.
- Attempting privilege escalation or credential theft.
- Investigating cloud or Active Directory environments.
-
Filtering: It filters out benign activities by excluding commands associated with known safe processes and updates.
-
Categorization: The query categorizes detected commands into risk levels (High, Medium, Low) based on their potential threat:
- High Risk: Commands related to credential hunting and domain reconnaissance.
- Medium Risk: Commands for user/group enumeration and network discovery.
- Low Risk: General system information gathering.
-
Aggregation and Scoring: It aggregates the data by device and account, counting unique commands and calculating a risk score based on the detected categories.
-
Severity Classification: Based on the risk score and the duration of the activity, it assigns a severity level (Critical, High, Medium) to the detected behavior.
-
Output: Finally, it outputs a list of suspicious activities with details like timestamp, device name, account name, severity, risk score, and the commands detected, sorted by risk score.
This query helps security analysts identify potential security breaches by focusing on clusters of suspicious activities rather than isolated events, making it a powerful tool for detecting active threats with minimal false positives.
Details

Benjamin Zulliger
Released: March 10, 2026
Tables
Keywords
Operators