Windows Scheduled Tasks for DFIR Investigations

February 28, 2024

Welcome to the first post in a multi-part series about Windows Scheduled Tasks artifacts. This post aims to provide you with everything you will need to know about:

  • How scheduled tasks work on windows
  • How threat actors abuse scheduled tasks
  • Scheduled task artifacts
  • How Cyber Triage Helps

In subsequent posts we will dive into each artifact in detail and discuss:

  • The artifacts structure and format
  • How scheduled tasks can be processed
  • Scenarios where scheduled tasks produce unexpected results

How Scheduled Tasks Work On Windows

Task Scheduler is a feature in Windows that allows a user or application to automate the execution of a program based on a trigger event. The most common way for users to interact with tasks is through the Task Scheduler GUI (taskschd.msc), via command line tools like schtasks.exe and at.exe, or PowerShell’s Scheduled Tasks cmdlets. They can also be programmatically accessed via Windows API. Microsoft provides numerous examples here.

There are two main components that make up the Task Scheduler infrastructure:

  • Task definitions
  • Task scheduling

Task definitions describe the task that is to be performed. Microsoft has great documentation on the various components that make up a task definition. The core aspects of tasks we are interested in for intrusion cases are: triggers, actions, and principles.

  • Triggers: can be defined as a time based trigger, such as a trigger that runs daily at 4 pm, or an event based trigger, such as a trigger that runs on system boot. A task can have multiple triggers.
  • Actions: In most cases, an action will point to an executable or script with optional arguments. In some cases, an action will be a ComHandler GUID, which requires looking up the related DLL in the Windows registry. A good write-up on how to do this can be found here. Note that a single task can have multiple actions. As a result, it is important to verify that the tool you use to examine scheduled tasks is capable of showing all actions and not just the first one.
  • Principles: The principles tell us what user a task can run as. Some tasks will specify a specific user, such as NT Authority\SYSTEM, while other tasks may specify a group. A group indicates that the task can be run by several users. This requires historical artifacts from the event logs to determine which users have run a task.

Task scheduling is how the system carries out the defined tasks. This job is performed by the Windows service “Schedule.” Upon start, the service loads all task definitions into its process memory. The Schedule service knows what trigger events to watch out for, what actions to perform, and who the actions should be run as. The task scheduler will be updated with newly created tasks, tasks that are modified, or tasks that are deleted under normal circumstances. If the scheduler does not know about the task, it will not be executed; this is important to remember, as we will discuss later in the post.

How Threat Actors Abuse Scheduled Tasks

Scheduled Tasks are well known for being used as a persistence technique that easily allows a threat actor to execute code for a given trigger (for example, when a user logs on or the system boots up). Newly created tasks can blend into the environment since Windows ships with many preconfigured tasks, and numerous applications use tasks as a method to perform actions such as checking for updates or ensuring they startup automatically. Moreover, threat actors do not always need to create a new task. In some cases, an existing task can be modified by updating an existing action or adding a new action.

Aside from persistence, tasks can be used to execute commands remotely. A common tool used for this is Impackets atexec.py script. Additionally, given the right permissions, scheduled tasks provide an easy way to perform privilege escalation to SYSTEM. MITRE ATT&CK technique T1053-005 provides numerous examples of the various task abuses we have discussed.

Scheduled Task Artifacts

There are several sources of evidence that can be examined when it comes to scheduled tasks and task execution. We will break these sources up into 3 groups:

  • Tasks defined on disk
  • Tasks defined in memory
  • Task history from event logs

Tasks Defined On Disk

A host’s current set of task definitions can be found in 4 places:

  • C:\windows\tasks
  • C:\windows\system32\tasks
  • C:\windows\system32\config\SOFTWARE
    • HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks
    • HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree
  • %localappdata%\Microsoft\Windows\PowerShell\ScheduledJobs

This data represents the set of tasks that will be loaded by the Scheduler service the next time it is started. In most cases, it will match up with the set of tasks that are currently managed by Task Scheduler, but there are scenarios where this is not true.

Windows XP

Windows XP task definitions are stored in C:\windows\tasks. They are binary files with a .job extension, and they follow Task Sheduler 1.0 specifications.

Window Vista & Windows 7

Starting with Windows Vista, Task Scheduler 2.0 was introduced. Tasks created by the newer task scheduler are stored in C:\windows\system32\tasks. This folder and its subfolders contain XML-defined tasks with no file extension. C:\windows\tasks can still contain .job task definitions for tasks created using Task Scheduler 1.0. This is most common with the use of at.exe.

Windows 8 and newer

A significant change was introduced to the storage location of task definitions in Windows 8. Although XML files continue to populate C:\windows\System32\Tasks, the true location of where the Task scheduler loads task definitions is from the registry. This is easily demonstrated by deleting the XML file on disk and issuing a restart of the system. The supposedly “deleted” task still appears in the Task Scheduler UI and runs. After the restart, you can check the Software registry hive and delete or modify data there. Then, you can see if the task scheduler sees the updated task (after a reboot).

There are two main registry keys to focus on.

  1. HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree

    The Tree key contains the task GUID ID, index value, and the Security Descriptor. We won’t go into details about this key until our deep dive post, but it’s important to look for missing or non-standard Index and SD values as attackers like HAFNIUM have been seen modifying them to hide tasks from standard task viewing methods. More details can be found here.
  2. HKLM\Software\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks

    The Tasks key is where your standard task components, such as: triggers, actions, and principles, can be found. Parsing and the data will be covered in our follow up deep-dive.

PowerShell Scheduled Jobs

Last up is PowerShell Scheduled Jobs. These are a hybrid between a PowerShell job and a scheduled task. We will cover these more in depth in our follow-up series, but here are a few key takeaways:

  • This kind of scheduled task is created just like any other scheduled task
    • Task will be placed in registry
    • Task XML will be placed in C:\Windows\System32\Tasks\Microsoft\Windows\Powershell\ScheduledJobs
  • A PowerShell scheduled job is also created. It has a XML-defined job similar to the task definition
    • %localappdata%\Microsoft\Windows\PowerShell\ScheduledJobs
  • PowerShell scheduled job is only executed once called from the scheduled task via the task action
  • Without the PowerShell job details you have no insight into what the scheduled task is executing indirectly
Powershell.exe -NoLogo -NonInteractive -WindowStyle Hidden -Command “Import-Module PSScheduledJob; $jobDef = [Microsoft.PowerShell.ScheduledJob.ScheduledJobDefinition]::LoadFromStore(‘4femfj9s’, ‘C:\Users\Administrator\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs’); $jobDef.Run()”

An example of a scheduled task action created by PowerShell’s scheduled job. Note that it does not show what file or script will be executed. To figure that out, the PowerShell scheduled job must be looked at. 


Powershell scheduled job xml definition found at C:
\Users\Administrator\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs\4femfj9s\ScheduledJobDefinition.xml. InvocationParam_FilePath and InvocationPara_ScriptBlock will point to the actions for the job.

Tasks Defined In Memory

The primary source of evidence pertaining to scheduled tasks in memory is the process memory of the svchost hosting the Schedule service. This data represents the list of active scheduled tasks that can be acted upon by the Task Scheduler service. By looking at the svchost arguments, we can determine the correct process to analyze. Generally, on newer systems, the arguments will contain “-s Schedule.” While on older systems where “-s Schedule” is not found, you’ll want  “-k netsvcs.” More information on what these arguments mean and how svchost loads service can be found here.

Unfortunately, most tools do not provide the ability to parse task data out of svchost process memory. The only tool we are aware of that does this is a PoC PowerShell script TaskHunter.ps1. Other tools like Volatility have modules such as Autoruns.py (.xml files) and schtasks.py (.job files) that rely on finding task files still in system memory. The other approach we have seen involves enumerating the registry, which is the approach that MemProcFS uses. Both have similar pitfalls to their on-disk counterparts.

While memory is the source of truth for what tasks are able to be executed, in many cases, it is not needed. This is because tasks are not persistent without the on-disk definitions. A scheduled task that only exists in memory will not survive a reboot of the system or service. If an attacker is using scheduled tasks for persistence, then the on-disk artifacts are all that is needed. Memory based task artifacts become more interesting when dealing with advanced threat actors or scenarios where a task was deleted off disk, only exists in memory, and proper logging is disabled. However, as we will see next, proper logging will also alleviate this need.

 

Task History from Event Logs

Our last group of sources cover historical artifacts from the Windows Event Log. The data is broken up into two types:

  • Historical tasks artifacts
  • Historical task execution artifacts

Historical task artifacts

Historical task artifacts represent task definitions that existed at a point in time. This could be a newly created task or it could be a task that has been updated or deleted. Historical evidence is very important for detecting attacker activity that relies on temporary tasks such as impackets atexec.py.

When atexec.py is used, a task is created, a program is run, and the task is deleted. This is done for every command an attacker wants to run via atexec.py. The artifacts we have previously discussed are not well suited to track this activity. We could get lucky and find some evidence still residing in memory, but we prefer to not have to rely on luck.

There are three events that we care about from the Windows Security event log:

Both task created and task updated events provide the full XML definition of the task within the event. This allows us to recreate deleted tasks as well as observe changes to tasks where an attacker adds a new action to an existing task. The task deleted event is less interesting than the other two, mainly because it tends not to have the XML data, even though documentation indicates that it should. Nonetheless, it can still be useful. By comparing the creation and deletion times, we can get an idea of what tasks are temporary vs long lived tasks. This can be interesting to look at when looking for non-persistent malicious tasks.

It’s important to note that these events are disabled by default. For Windows to record these events, the “Audit Other Object Access Events” audit setting must be set to Success.

Historical task execution artifacts

Historical task execution artifacts represent evidence of when a task or task action ran.

There are two events that we care about from Microsoft-Windows-TaskScheduler/Operational:

Of the two events, Event 200 (Task started) is more interesting as it provides us with the path of the program that was run. It does not include the arguments in the event, which is important to note. Secondly, it does not contain the user the action was run as. That is where Event 100 comes in. Event 100 contains the task name, a task instance GUID, and the user. This data can be correlated to event 200, which also includes the task name and instance GUID, to get the user that ran a specific action. Unfortunately, the scheduled task history event log is disabled by default. It can be enabled by enabling “task history” from the task scheduler GUI or by enabling the Microsoft-Windows-TaskScheduler/Operational event log directory from the Windows Event viewer.

 

How Cyber Triage Helps

In summary, attackers often use scheduled tasks as a form of persistence within an environment. Examining scheduled tasks on large numbers of hosts can be tedious and time consuming.

Tasks in memory represent the set of tasks that can be acted upon by the scheduler service. The tasks on-disk represent the set of persistent tasks and, in most cases, match the set of tasks loaded in memory. The tasks found in event logs represent tasks that existed on the system at some point in time but may not exist anymore.

Analysis of each of these groups yield different investigative contexts. Also, each group has several artifacts that must be analyzed to find all the evidence surrounding scheduled tasks, and each artifact may be in a unique location and format. Its availability may differ depending on OS version, audit settings, and how the tasks were created.

This is a lot of information for any one investigator or team of investigators to remember for just Scheduled Task artifacts, not to mention the many other important artifacts that come into play during an investigation. With all of this said, Cyber Triage is here to help! Cyber Triage helps investigators analyze Scheduled Tasks in the following ways:

  • Comprehensive collection of source files
  • Comprehensive parsing of source files into artifacts
  • Intelligent scoring of artifacts based on sound investigative methodology and heuristics

 

Comprehensive collection of source files

Our Cyber Triage collector is a single windows executable that is deployed on demand as needed. You can start a collector via our app or using your own infrastructure (Ex. EDR or SCCM). When run, it collects all of the scheduled task sources we discussed as well as many other artifacts. It also goes a step further and collects all of the scripts and exes that are referenced in the scheduled tasks, so malware scanning can be performed on executables and manual analysis of scripts. This is unique to our Cyber Triage collector as many other collectors such as Kape and Velociraptor just target specific files that are predefined or user defined. Within the Cyber Triage app, you are able to export all of the scheduled tasks source files if you’d like to do your own analysis as well.

Comprehensive parsing of source files into artifacts

Cyber Triage collects all of the task-related source files for you and parses them to create artifacts for review. We currently create “Triggered Task” artifacts from all of the sources we discussed aside from Powershell scheduled jobs (we do collect them). This means there is no need for you to find multiple tools to handle event log tasks, Binary .job tasks, XML tasks, and registry based tasks. We provide all of that functionality in a single tool.

Once parsed, you can easily review all tasks within Cyber Triage. The above picture demonstrates that high level actions, task names, and creation time can be viewed. Selecting a task will result in more details about the triggers, the user associated with the task, and malware score of the associated file. Investigators can quickly jump to related items using the related info viewer. The numerous tabs provide details to see if the task has been executed (Process tab), details on the associated file (File tab), and if the task is seen on other Hosts (Other Occurrences). Additionally, you can click on the Sources tab and see where the task information came from. The below picture shows that we have a task that came from both the XML file on-disk as well as the task creation event from the event logs. You can click on the links to view the task details or jump to the XML file to see the raw data.

Intelligent scoring of artifacts

On top of the collection and parsing, we apply scoring to bubble up tasks of interest, so you can ignore the 100s of other tasks that are irrelevant. This provides a significant advantage to investigators as they can quickly focus on a smaller set of bad or suspicious tasks to make a final determination if they are relevant or not. Some of the many heuristics that we apply to tasks are:

  • Mark task as suspicious if created in last 30 days
  • Mark task as suspicious if ran from an unusual location
  • Mark task as suspicious if action runs .lnk file
  • Mark task as bad if created by impackets atexec
  • Mark task as bad if its hidden from task scheduler (related to HAFNIUM )
  • Mark task as bad based on user defined bad list rules

To try intelligent artifact scoring, start your free trial of Cyber Triage today!