Scheduled Task Persistence on Windows 11: The Attack and Its Artifacts

Lab details

Type
Attack and detection
Level
Intermediate
Time
45 min
Verified on
Windows 11 24H2 (build 26100.2894), Hyper-V VM on an internal-only virtual switch, Sysmon 15.15

Before you start

  • The isolated analysis VM from the lab-build guide
  • An administrator account inside the VM
  • Autoruns and Sysmon (both free from Microsoft)

Tools used

Why This Article Has Two Halves

Most writing about scheduled-task persistence shows either the attack or the detection, never both against the same event. That split is why detection guidance so often fails to match what an attacker actually does, and why offensive write-ups leave defenders with no idea what to look for.

Here the same task is created and then hunted, in one place, on one machine. The attack half runs only inside the isolated lab VM — see the authorization notice above. The value is in the second half: knowing exactly what this technique writes to disk, the registry, and the event log.

Technique: MITRE ATT&CK T1053.005, Scheduled Task.

Where a Scheduled Task Actually Lives

Before creating one, understand the three places Windows stores it — because the detection depends on them agreeing:

  1. C:\Windows\System32\Tasks\<TaskName> — the task definition, as an XML file on disk.
  2. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\<TaskName> — the entry the Task Scheduler UI reads to build its list.
  3. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{GUID} — the operative definition, keyed by GUID, holding the actual trigger and action.

When these three disagree, the disagreement is the finding. That fact is the whole basis of the detection, and the attack below deliberately creates such a disagreement.

The Attack (isolated lab VM only)

Step 1: Register a task that runs at logon

Inside the VM, from an elevated prompt. This registers a task that launches PowerShell at every user logon — the shape of countless real persistence mechanisms, with a benign payload standing in for the malicious one:

schtasks /create /tn "MicrosoftEdgeUpdateTaskMachineCore" /tr "powershell.exe -WindowStyle Hidden -Command \"Add-Content C:\lab\beacon.txt (Get-Date)\"" /sc onlogon /ru SYSTEM /rl HIGHEST

Note the choices an attacker makes here, because each one is also a detection opportunity:

  • A name that impersonates a real Microsoft task. Blends into a casual glance at the task list.
  • /ru SYSTEM — runs as the highest-privilege account.
  • -WindowStyle Hidden — no visible window.
  • The action is an interpreter, not a normal executable. Real software schedules its own binary; interpreters running encoded or obscured commands are the tell.

Step 2: Hide it from the Task Scheduler UI

This is the step that separates a lazy implant from a deliberate one. Delete the task's entry from the Tree key while leaving the Tasks\{GUID} definition intact:

# Find the GUID for the task under TaskCache\Tree, then remove the Tree entry.
# The task still exists and still runs — it just no longer appears in taskschd.msc.

The mechanism: the Task Scheduler UI enumerates from Tree. Removing the Tree entry (while keeping Tasks\{GUID}) means the task runs at every logon but does not appear in taskschd.msc. This is a documented, real-world technique, and it is exactly why UI-based checking is insufficient.

The Artifacts It Left

Now revert your mindset to the defender and find what the attack above wrote. Everything from here is safe on any machine.

Step 3: Enable and read the Task Scheduler operational log

Event Viewer → Applications and Services Logs → Microsoft → Windows → TaskScheduler → Operational. Enable the log if it is off.

Event 106 — "Task registered" — records the creation with the task name and the user who created it, at the time it happened. This fired when Step 1 ran, and hiding the task from the UI in Step 2 does not remove this event. The registration is already recorded.

Step 4: Read event 4698 in the Security log

If audit policy was enabled (it is off by default — auditpol /set /subcategory:"Other Object Access Events" /success:enable), Security event 4698 captured the full task XML at creation, including the <Command>, the <Principal> showing S-1-5-18 (SYSTEM), and the logon trigger.

The event exists independently of the task's current visibility. The attacker hid the task from the UI after these events were written — the log is the record they could not retroactively edit.

Full detail on these events: event IDs 7045 and 4698.

Step 5: Compare the three storage locations

This is the detection that catches the hidden task specifically. Enumerate all three locations and look for the mismatch the attack created:

# 1. The XML files on disk
Get-ChildItem C:\Windows\System32\Tasks -Recurse -File

# 2. The Tree entries (what the UI shows)
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree" -Recurse

# 3. The actual task definitions
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks"

The hidden task appears in the on-disk Tasks folder and in TaskCache\Tasks\{GUID}, but not in TaskCache\Tree. That is the signature: a task definition with no corresponding Tree entry runs but does not show in the UI. No legitimate task does this.

Step 6: Confirm with Autoruns

Run Autoruns elevated and open the Scheduled Tasks tab. Autoruns reads the underlying Tasks data, not the UI's Tree view, so the hidden task appears here even though taskschd.msc does not show it. The gap between what Autoruns shows and what the UI shows is the finding, made visible without manual registry work.

The XML file's creation timestamp in C:\Windows\System32\Tasks\ dates the task independently of any log, and $UsnJrnl recorded the file's creation with a timestamp even if the file is later deleted.

Detection Summary

For a machine you defend, the durable signals from this technique are:

  • TaskScheduler Operational event 106 and Security event 4698 at creation — enable both; 4698 needs audit policy on
  • A TaskCache\Tasks\{GUID} entry with no matching TaskCache\Tree entry — the hidden-task signature, catchable on a schedule with the Step 5 comparison
  • Autoruns showing a task the UI does not — the manual version of the same check
  • A task action that is an interpreter (powershell, cmd, mshta, rundll32), especially running as SYSTEM at logon or boot
  • The XML file creation time in System32\Tasks, which survives log rotation

Related

FAQ

Does hiding the task from Task Scheduler remove the log entries?

No, and that is the core of the detection. The registration events — TaskScheduler Operational 106 and Security 4698 — are written when the task is created, before it is hidden. Removing the TaskCache\Tree entry hides the task from the UI afterwards but cannot retroactively delete events already recorded in the log.

How do I find a scheduled task that does not appear in taskschd.msc?

Compare the three storage locations: the XML files in C:\Windows\System32\Tasks\, the TaskCache\Tree registry key (what the UI shows), and the TaskCache\Tasks\{GUID} key (the real definitions). A task present in Tasks but absent from Tree runs without showing in the UI. Autoruns performs this comparison automatically and lists such tasks.

Does a scheduled task survive a Windows reset?

A full reset or clean reinstall removes it — the task lives on the system drive that gets wiped. "Reset this PC" with the "keep my files" option is less certain, since it preserves parts of the user environment; verify afterwards rather than assuming. A task does survive normal reboots, updates, and antivirus scans that do not specifically target it, which is the entire point of the technique.

Is creating a scheduled task always malicious?

No — it is one of the most common legitimate mechanisms on Windows. Updaters, backup tools, and maintenance routines all use scheduled tasks heavily. What distinguishes a malicious one is the combination: a hidden task, running as SYSTEM, triggered at logon or boot, whose action is an interpreter with an obscured command, impersonating a system task name. Any one of those can be innocent; together they are not.