Windows Scheduled Task XML Explained, Element by Element
Read a Task Scheduler XML file like an investigator: RegistrationInfo, Triggers, Principals, Settings and Actions, and the zoneless local times in Date and StartBoundary.
TL;DR. A task XML has five sections that matter: RegistrationInfo (who and when, as claimed), Triggers (when it fires), Principals (which account, which privileges), Settings (hidden, enabled, limits) and Actions (what runs). Two traps: Date and StartBoundary are usually local time with no offset, and Author is free text. Read the file as a set of claims and check them against TaskCache and event logs.
The format is documented in Microsoft's Task Scheduler Schema, with the namespace http://schemas.microsoft.com/windows/2004/02/mit/task. This article reads it from an investigator's point of view. Where the files live is in scheduled task locations and acquisition.
A minimal example
A task registered with schtasks on a workstation typically looks like this. It is trimmed from the fictional FIN-WKS-07 sample used throughout this site, set to UTC+02:00:
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2026-09-14T12:09:58.4413872</Date>
<Author>FIN-WKS-07\svc_backup</Author>
<Description>Intel graphics telemetry</Description>
<URI>\IntelGfxTelemetry</URI>
</RegistrationInfo>
<Triggers>
<BootTrigger><Enabled>true</Enabled></BootTrigger>
<TimeTrigger>
<Repetition><Interval>PT1H</Interval></Repetition>
<StartBoundary>2026-09-14T12:15:00</StartBoundary>
<Enabled>true</Enabled>
</TimeTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<Hidden>true</Hidden>
<Enabled>true</Enabled>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\ProgramData\Intel\m64.exe</Command>
<Arguments>-svc</Arguments>
</Exec>
</Actions>
</Task>
The file on disk is usually UTF-16LE with a byte order mark, even though the XML declaration is sometimes copied around as UTF-8. A parser that assumes UTF-8 will show garbage; that alone is a reason to use a tool built for the format.
RegistrationInfo: the claimed metadata
| Element | What it says | How far to trust it |
|---|---|---|
URI | Task path in the library, e.g. \Microsoft\Windows\Defrag\ScheduledDefrag | Should match the file path and the TaskCache Tree key; a mismatch is worth noting |
Author | Who registered the task | Free text. Console and schtasks write DOMAIN\user; installers often write a vendor name; some Microsoft tasks use a resource reference like $(@%SystemRoot%\system32\...) |
Date | Registration date | Usually local time, no offset (see below) |
Description | Human description | Free text; attackers copy Microsoft wording |
SecurityDescriptor | Optional SDDL for the task | Rare in XML; the effective descriptor is TaskCache's SD value |
Source, Version, Documentation | Optional | Occasionally useful for vendor attribution |
Practitioner notes such as Psmths' Windows forensic artifacts point out that the Author of a remotely created task can reveal the originating account, but nothing prevents an XML import from carrying a forged author. Compare with the SubjectUserName of event 4698 or 106 when you have them.
Triggers: when it fires
The schema allows up to 48 triggers per task: BootTrigger, LogonTrigger, RegistrationTrigger, IdleTrigger, TimeTrigger, CalendarTrigger, EventTrigger and SessionStateChangeTrigger. All share Enabled, StartBoundary, EndBoundary, Repetition and ExecutionTimeLimit.
Investigative reading:
- Boot and Logon triggers are classic persistence. A
LogonTriggerwithoutUserIdfires for any user. - RegistrationTrigger fires once when the task is registered: a common way to run something immediately while looking like a scheduled job.
- TimeTrigger with a short
Repetitioninterval (for examplePT5M) is a beacon-like pattern. - EventTrigger carries an XPath
Subscriptionquery; it can make a task fire on a specific event ID, which is easy to miss. - SessionStateChangeTrigger (
SessionUnlock,RemoteConnect) is less common and worth a second look.
Principals: which account, which privileges
| Element | Values | Meaning |
|---|---|---|
UserId | Account name or SID (S-1-5-18 is SYSTEM, S-1-5-19 LOCAL SERVICE, S-1-5-20 NETWORK SERVICE) | The security context |
GroupId | Group name or SID | Runs for members of a group, e.g. S-1-5-32-545 (Users) |
LogonType | InteractiveToken, Password, S4U, InteractiveTokenOrPassword | How the account is logged on; see S4U |
RunLevel | LeastPrivilege, HighestAvailable | Whether an administrator token is used; see run level |
A non-Microsoft task running as S-1-5-18 with HighestAvailable is the combination to sort first. LogonType Password means credentials were stored so the task can run while the user is logged off; Microsoft's guidance on event 4698 recommends alerting on it because the stored password is recoverable by an administrator.
Settings: the switches that hide or throttle
The elements to read first are Hidden, Enabled, ExecutionTimeLimit (PT0S means no limit), MultipleInstancesPolicy, StartWhenAvailable, DisallowStartIfOnBatteries, WakeToRun, RunOnlyIfNetworkAvailable and DeleteExpiredTaskAfter.
Hidden=true only hides the task in the console unless "Show Hidden Tasks" is ticked. It does not hide it from schtasks, the API or the registry. The stronger hiding techniques live in the registry, see Tarrask and hidden scheduled tasks. DeleteExpiredTaskAfter with an EndBoundary makes a task delete itself: its absence later is not evidence it never existed.
Actions: what runs
| Action | Content | Notes |
|---|---|---|
Exec | Command, Arguments, WorkingDirectory | The vast majority of tasks |
ComHandler | ClassId (CLSID), optional Data | Runs a COM object, typically a DLL loaded in taskhostw.exe; see COM handler action |
SendEmail | Server, To, Subject... | Deprecated by Microsoft; rare on modern systems |
ShowMessage | Title, Body | Deprecated; rare |
The Actions element has a Context attribute naming the principal. Up to 32 actions are allowed and they run in order, so read them all: a benign first action can precede a malicious second one.
For ComHandler, resolve the CLSID in the SOFTWARE hive (Classes\CLSID\{...}\InprocServer32) to find the DLL. Hijacking a CLSID that a legitimate task already uses is a quieter form of persistence than creating a new task.
Time: zoneless local values
The schema types Date, StartBoundary and EndBoundary as xs:dateTime. The scripting API documents the format YYYY-MM-DDTHH:MM:SS(+-)HH:MM with an offset (Trigger.StartBoundary). In practice, tasks created in the console or with schtasks store no offset, and the time is the machine's local time when the task was saved. Some tasks store a Z or an explicit offset; most do not.
This matters as soon as you compare with UTC sources. In Microsoft's 4698 sample, the event's TimeCreated is 2015-09-23T02:03:06Z and the XML Date is 2015-09-22T19:03:06.9258653: the same instant, seven hours apart.
Ways to recover the offset:
- TaskCache:
DynamicInfoholds a creation FILETIME in UTC for the same task; the difference withDateis the offset, rounded to the nearest quarter hour. See TaskCache registry forensics. - SYSTEM hive:
TimeZoneInformationgives the configured zone; remember daylight saving applies at the date of the event, not the date of collection. - Event 4698:
TimeCreatedin UTC against the embeddedDate.
The Scheduled Tasks Parser does the first automatically: it compares XML registration dates with TaskCache FILETIMEs, proposes an offset, and lets you override it.
StartBoundary has an extra subtlety: it is when the trigger becomes active, which can be in the past (tasks created "now" often get the creation minute) or far in the future. It is not a run time. Run times come from TaskCache DynamicInfo and events 200/201.
FAQ
What time zone is the Date in a scheduled task XML?
Usually none. The schema allows an offset, but tasks created in the console or with schtasks typically store local time without any offset. Infer the offset from a UTC source, such as TaskCache FILETIMEs or event 4698's TimeCreated, before comparing with other evidence.
Does the Author field prove who created the task?
No. Author is free text written by whatever registered the task. The console and schtasks fill it with the creating account, but a script or an imported XML can put anything there. Treat it as a claim to verify against event logs.
What does Hidden=true mean?
The task is not shown in the Task Scheduler console unless the user enables Show Hidden Tasks. It is still listed by schtasks and by the API. Many Microsoft tasks are hidden, so it matters mostly for non-Microsoft tasks.