Detecting Persistence via Windows Scheduled Task Creation Using KQL

Hunt for adversary persistence via unauthorized Windows Scheduled Tasks by parsing Security Event ID 4698 and DeviceProcessEvents in KQL.

Dikshant Lather
2 min read ·
Detecting Persistence via Windows Scheduled Task Creation Using KQL

Detecting Persistence via Windows Scheduled Task Creation Using KQL

Scheduled Tasks are among the most prevalent persistence and privilege escalation mechanisms utilized by threat actors. Once an attacker gains administrative privileges, they register a task scheduled to trigger at system startup or at recurring intervals, maintaining a foothold across system reboots.

This article provides practical KQL queries for both Windows Event Logs (SecurityEvent 4698) and Defender for Endpoint (DeviceProcessEvents).


Method 1: Analyzing Windows Security Event Log (Event ID 4698)

Windows Event 4698 triggers whenever a scheduled task is created. The task configuration payload is embedded as raw XML within the EventData field.

SecurityEvent
| where TimeGenerated >= ago(7d)
| where EventID == 4698
| parse EventData with * '<Data Name="TaskName">' TaskName '</Data>' *
| parse EventData with * '<Command>' TaskCommand '</Command>' *
| parse EventData with * '<Arguments>' TaskArguments '</Arguments>' *
| parse EventData with * '<UserId>' RunAsUser '</UserId>' *
| where TaskCommand has_any ("powershell", "cmd", "wscript", "cscript", "mshta", "rundll32", "certutil", "curl", "bitsadmin")
    or TaskCommand startswith "C:\\Users\\"
    or TaskCommand startswith "C:\\Windows\\Temp\\"
| project TimeGenerated, Computer, SubjectUserName, TaskName, TaskCommand, TaskArguments, RunAsUser
| order by TimeGenerated desc

Method 2: Detecting Command-Line Creation (schtasks.exe)

Threat actors frequently leverage schtasks /create via automated batch or PowerShell scripts:

DeviceProcessEvents
| where TimeGenerated >= ago(7d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has_all ("/create", "/tn")
| extend TaskName = extract(@"/tn\s+[""']?([^""'\s]+)", 1, ProcessCommandLine),
         TaskRun = extract(@"/tr\s+[""']?([^""']+)[""']?", 1, ProcessCommandLine)
| where TaskRun has_any ("powershell", "cmd.exe", ".vbs", ".bat", ".ps1", "http:", "https:")
| project TimeGenerated, DeviceName, AccountName, ProcessCommandLine, TaskName, TaskRun, InitiatingProcessFileName

False Positive Tuning

  • Legitimate software installers create tasks periodically.
  • Exclude known vendor signatures by checking whether TaskRun points to signed executable binaries in Program Files.

MITRE ATT&CK Mapping

  • Tactic: Persistence (TA0003), Privilege Escalation (TA0004), Execution (TA0002)
  • Technique: Scheduled Task/Job: Scheduled Task (T1053.005)
Dikshant Lather
Written by

Dikshant Lather

Cyber Security & AI Architect

Responses (0)

Join the technical conversation or share implementation thoughts.

What are your thoughts?

Sign in to join the technical discussion or share feedback.

There are currently no responses for this story. Be the first to respond.