Home / Blog / Threat Hunting

Advanced Threat Hunting

How to Hunt Malicious and Hidden Scheduled Tasks on Windows

A practical hunt for T1053.005: stack scheduled tasks across hosts, spot risky actions, catch tasks hidden by SD value removal and sweep the registry for what your logs missed.

Scheduled tasks give attackers a simple way to run code and survive a reboot, and they blend in well because Windows and most software create plenty of their own. This hunt is for analysts and threat hunters who want to find the few tasks that do not belong, including tasks hidden from the usual admin tools.

TechniqueT1053.005, Scheduled Task/Job: Scheduled Task
Best dataEvent 4698, Sysmon registry events, MDE telemetry
Hunt outputA triaged task list and new detections
>

Scenario: An intrusion report describes an actor persisting through scheduled tasks with names that imitate Windows updates, launched from user-writable paths. You have no matching alerts. Your lead asks you to check whether anything similar exists across 3,000 Windows endpoints, including tasks that schtasks /query would not show.

Form your hunt hypotheses

A hunt across all tasks without a clear question quickly becomes a noisy list. Start with specific, testable hypotheses, then choose the data that can prove or disprove each one.

Rare tasksA malicious task will usually exist on very few hosts, unlike software updaters deployed across the estate.
Risky actionsIts action may call a script host or living-off-the-land binary, or run from a user-writable folder.
ConcealmentIts registry metadata may have been altered so that normal tools no longer list it.

Confirm your data sources

Check your visibility first, because a clean result means little if the logs were never collected. Microsoft notes that neither Event 4698 in the Security log nor the Task Scheduler Operational log is audited by default.

Windows Security logEvents 4698 to 4702 record task creation, deletion, enabling, disabling and updates. Event 4698 includes the task XML.
Task Scheduler OperationalEvent 106 records registration, 140 updates and 141 deletion. Events 200 and 201 cover action start and completion.
SysmonEvent 1 shows process creation, 11 task file creation and 12 to 14 changes under the TaskCache registry keys.
Defender for EndpointDeviceEvents records ScheduledTaskCreated and ScheduledTaskUpdated, and DeviceRegistryEvents records registry changes.

If auditing is missing, raise the gap and agree a fix with the team that owns your endpoint policy. On a single test host, the settings look like this:

Elevated command promptCMD
auditpol /set /subcategory:"Other Object Access Events" /success:enable
wevtutil sl Microsoft-Windows-TaskScheduler/Operational /e:true
!

Watch out: Sysmon only logs what its configuration includes. Before you rely on a Sysmon hunt, confirm your config captures registry events under \Schedule\TaskCache\Tree\. Otherwise, a lack of results tells you nothing.

Stack tasks to find the rare ones

Stacking groups events by task name and counts the hosts involved. Tasks deployed by IT tend to show up widely, while targeted persistence tends to sit at the low end. Low prevalence is a lead, not a verdict, because small teams install niche software too.

Defender for Endpoint

Defender XDR advanced huntingKQL
DeviceEvents
| where Timestamp > ago(30d)
| where ActionType in ("ScheduledTaskCreated", "ScheduledTaskUpdated")
| extend Fields = parse_json(AdditionalFields)
| extend TaskName = tostring(Fields.TaskName),
    TaskContent = tostring(Fields.TaskContent)
| summarize Hosts = dcount(DeviceName), Events = count(),
    FirstSeen = min(Timestamp), LastSeen = max(Timestamp),
    Creators = make_set(InitiatingProcessAccountName, 10),
    SampleContent = take_any(TaskContent)
    by TaskName
| where Hosts <= 3
| sort by Hosts asc, FirstSeen desc

Splunk

This version assumes the Windows add-on's XML format. Your index and field names may differ.

SplunkSPL
index=wineventlog source="XmlWinEventLog:Security" EventCode IN (4698, 4702)
| stats dc(host) as hosts count min(_time) as firstSeen max(_time) as lastSeen
    values(SubjectUserName) as creators by TaskName
| where hosts <= 3
| sort hosts, -firstSeen
| convert ctime(firstSeen) ctime(lastSeen)
+

Tip: Tasks with random suffixes, such as per-user updater tasks, can fragment the count and look rare. Normalise names by stripping GUIDs and SIDs if you see this, and keep your list of benign patterns so the next hunt starts ahead.

Inspect task actions and context

Next, look at what each task runs. The Security events store the XML in TaskContent for 4698 and TaskContentNew for 4702. In Sentinel, the SecurityEvent table has no dedicated columns for these fields, so extract them from EventData.

Microsoft SentinelKQL
SecurityEvent
| where TimeGenerated > ago(30d)
| where EventID in (4698, 4702)
| extend TaskName = extract(@'<Data Name="TaskName">([^<]+)</Data>', 1, EventData)
| extend TaskContent = extract(@'(?s)<Data Name="TaskContent(?:New)?">(.*?)</Data>', 1, EventData)
| where TaskContent matches regex @'(?i)(powershell|pwsh|cmd\.exe|mshta|rundll32|regsvr32|wscript|cscript|certutil|bitsadmin)'
    or TaskContent matches regex @'(?i)\\(Users\\Public|AppData|ProgramData|Temp)\\'
    or TaskName matches regex @'^\\[^\\]+$'
| project TimeGenerated, Computer, EventID, SubjectUserName, TaskName, TaskContent
| sort by TimeGenerated desc

The last condition matches tasks in the Task Scheduler Library root, which Microsoft's Event 4698 guidance recommends monitoring. It will match some legitimate software too, so review the results in context.

Review every candidate against these questions:

  • Does the name imitate a Microsoft task but sit outside the usual \Microsoft\Windows\ path?
  • Does the action use encoded commands, download content or call a script from a user-writable folder?
  • Does it run as SYSTEM or with highest privileges when its purpose does not need that?
  • Does the task XML contain <LogonType>Password</LogonType>, meaning that credentials were stored with the task?
  • Who created it, from which process, and was it a remote or unusual logon session?
  • Does the binary it launches have a valid signature and a known prevalence in your estate?

To see tasks in action rather than just their creation, look for suspicious children of the Task Scheduler service.

Defender XDR advanced huntingKQL
DeviceProcessEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName =~ "svchost.exe"
| where InitiatingProcessCommandLine has "Schedule"
| where FileName in~ ("powershell.exe", "pwsh.exe", "cmd.exe", "mshta.exe",
    "rundll32.exe", "regsvr32.exe", "wscript.exe", "cscript.exe",
    "certutil.exe", "bitsadmin.exe")
| summarize Runs = count(), Hosts = dcount(DeviceName),
    FirstSeen = min(Timestamp), LastSeen = max(Timestamp)
    by FileName, ProcessCommandLine, AccountName
| sort by Hosts asc, Runs asc

Hunt for hidden tasks

Every task has registry entries under the TaskCache keys. In 2022, Microsoft documented the Tarrask malware deleting a task's Security Descriptor (SD) value, which makes the task disappear from schtasks /query and Task Scheduler. Deleting the value needs SYSTEM context, so a successful deletion also suggests the attacker already holds high privileges on that host.

Task registry locationsRegistry
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\<TaskName>
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{GUID}

Catch the deletion in logs

Legitimate software has little reason to remove an SD value directly, so treat any hit as high priority until it is explained.

Defender XDR advanced huntingKQL
DeviceRegistryEvents
| where Timestamp > ago(30d)
| where ActionType == "RegistryValueDeleted"
| where RegistryKey contains @"\Schedule\TaskCache\Tree\"
    or PreviousRegistryKey contains @"\Schedule\TaskCache\Tree\"
| where RegistryValueName == "SD" or PreviousRegistryValueName == "SD"
| project Timestamp, DeviceName, RegistryKey, PreviousRegistryKey,
    InitiatingProcessAccountName, InitiatingProcessFileName,
    InitiatingProcessCommandLine
Splunk with SysmonSPL
index=sysmon source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=12
    EventType=DeleteValue TargetObject="*\\Schedule\\TaskCache\\Tree\\*\\SD"
| table _time, host, User, Image, TargetObject

Sweep hosts directly

Logs only show what happened during your retention period, while a hidden task may be older. Microsoft recommends checking the Tree key for tasks without an SD value. This read-only script does that and also flags Index set to 0, because MITRE notes that altered metadata such as Index can also hide tasks.

Run elevated, read-onlyPowerShell
$root = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree'

Get-ChildItem -Path $root -Recurse | ForEach-Object {
    $names = $_.GetValueNames()
    # Task keys have an Id value, folder keys do not
    if ($names -contains 'Id') {
        $missingSd = $names -notcontains 'SD'
        $index = $_.GetValue('Index')
        if ($missingSd -or $index -eq 0) {
            [pscustomobject]@{
                Computer  = $env:COMPUTERNAME
                TaskKey   = $_.Name
                TaskId    = $_.GetValue('Id')
                Index     = $index
                MissingSD = $missingSd
            }
        }
    }
}

Deploy it through your EDR live response or management tooling and collect the output centrally. Run it on a known-good build first, so you understand what a normal result looks like in your environment. For each hit, use TaskId to find the matching Tasks\{GUID} key, then collect any XML file under C:\Windows\System32\Tasks.

!

Watch out: Do not delete a suspicious task as soon as you find it. Record the registry keys, the task file, the binary it runs and any related logs first. Removing it early destroys evidence and may alert the attacker before you understand the scope.

Grade findings and respond

Grade each candidate on the evidence you have, then hand over anything confirmed with enough detail for responders to act.

Grade 1

Unusual

A rare task, but with a signed binary, a known owner and a clear business purpose.

Niche vendor updater
Grade 2

Suspicious

A risky action or unusual path, with no owner yet. Investigate the host, creator and binary.

Script run from ProgramData
Grade 3

Malicious

A hidden task, known bad content or links to other intrusion activity. Escalate as an incident.

SD value removed by SYSTEM

When a Grade 3 finding comes up, widen the search. Use the task name, command line, file hash and creating account as pivots across all hosts. Because removing the SD value requires SYSTEM context, also look for how the attacker gained that level of access.

Turn the hunt into detections

A hunt delivers lasting value when its best logic becomes a monitored rule. Some of these checks are precise enough to alert on, while others work better as scheduled reports.

Hunt logicExpected noiseSuggested use
SD value deleted under TaskCache\TreeLowHigh priority alert
Task XML with LogonType set to PasswordLow to mediumAlert, with exceptions for known service tasks
Task action runs a script host from a user-writable pathMediumAlert after tuning
New task in the Task Scheduler Library rootMedium to highDaily review
Task seen on three or fewer hostsHighWeekly hunt report

Write down the data sources, time range, hypotheses and findings, including anything you could not test. A hunt that finds nothing is still useful if it records what was checked and which visibility gaps remain.

Key takeaways

  1. Confirm that Event 4698, Task Scheduler Operational logs and any Sysmon registry rules are actually being collected.
  2. Stack task names across hosts to surface rare tasks, and treat rarity as a lead rather than proof.
  3. Inspect the task action, path, privileges, stored credentials and creator before making a call.
  4. Hunt for deletion of the SD value in logs, then sweep the registry directly for hidden tasks your logs may have missed.
  5. Preserve evidence before removing a task, and find out how the attacker gained the access they needed.
  6. Convert your most precise hunt logic into alerts and keep the noisier checks as scheduled reviews.

Run this hunt again on a regular schedule and after any intrusion report that mentions scheduled tasks. Each pass adds to your baseline of normal tasks, which makes the next unusual one easier to spot.