Monitoring Windows task scheduler to detect attack persistence

| by Awwal Ishiaku | Wazuh 4.3
Post icon

The Windows task scheduler is a tool in the Windows operating system that launches programs and executes predefined scripts at scheduled times or after specified time intervals. While Windows Task Scheduler is not malicious, adversaries can abuse this utility to create malicious jobs that may execute to accomplish their goals. This technique (T1053) is widely used amongst threat actors such as Agent Tesla, and APT3 who are usually successful because there is usually poor visibility in monitoring the Windows task scheduler. In many cases, the task scheduler is utilized to download and execute scripts that run directly in the memory without leaving artifacts in the persistent storage (hard disk, SSD) therefore making detection more difficult than usual.

There is a need to monitor the task scheduler in order to detect when new jobs are scheduled. This can be done by Wazuh with the Sysmon integration. Sysmon can be configured to generate event logs when new tasks are created on monitored endpoints. After Sysmon is configured on the endpoint, rules are created on Wazuh to trigger alerts when the event log indicates that a scheduled task is created. The event log generated by Sysmon usually specifies that a new task has been scheduled, and the name of the task is mentioned. The actual content of the task such as the action and arguments to be executed by the task is not mentioned in the event log from Sysmon, therefore the analyst has to do some more investigation outside of the dashboard to gain insight into what has been scheduled.

In order to make the job easier for the analyst, we will take it a step further by creating an active response script that will trigger when a new task is scheduled. The active response script will extract the content of the scheduled task from the endpoint and present it to the analyst on the dashboard.

We used Wazuh server 4.3.3 for this blog post. The prerequisites for following this guide are:

  • A Wazuh server. The Installation guide can be found here.
  • Wazuh agent running on a Windows endpoint. A Wazuh agent can be installed by following the guide in the documentation here.

The following sections of the blog post show how to do the following:

  • Setup and configure Sysmon on the Windows endpoint.
  • Create a rule to detect when a new task has been scheduled.
  • Create the active response script to analyze the newly created task.
  • Configure Wazuh server to trigger the active response script when a new scheduled task is created.
  • Create a rule to present the content of the scheduled task on the Wazuh dashboard.

Install Sysmon

Download Sysmon from Microsoft Sysinternals page. Also download this sysmon XML configuration file.

Install Sysmon with this configuration via Powershell as Administrator:

.\Sysmon.exe -accepteula -i .\sysmonconfig.xml

An entry is created in the Registry for every new task that is created by the task scheduler, and this activity is logged by Sysmon. A sample of one of the logs generated when a new scheduled task is created is shown below. Areas of interest that are useful in creating a detection rule are highlighted:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385f-c22a-43e0-bf4c-06f5698ffbd9}" /> 
  <EventID>12</EventID> 
  <Version>2</Version> 
  <Level>4</Level> 
  <Task>12</Task> 
  <Opcode>0</Opcode> 
  <Keywords>0x8000000000000000</Keywords> 
  <TimeCreated SystemTime="2022-04-13T11:59:41.5535365Z" /> 
  <EventRecordID>184</EventRecordID> 
  <Correlation /> 
  <Execution ProcessID="5944" ThreadID="4436" /> 
  <Channel>Microsoft-Windows-Sysmon/Operational</Channel> 
  <Computer>DESKTOP-4E0BQFT</Computer> 
  <Security UserID="S-1-5-18" /> 
  </System>
- <EventData>
  <Data Name="RuleName">technique_id=T1053,technique_name=Scheduled Task</Data> 
  <Data Name="EventType">CreateKey</Data> 
  <Data Name="UtcTime">2022-04-13 11:59:41.545</Data> 
  <Data Name="ProcessGuid">{76e50f37-2fc0-6244-1300-000000000300}</Data> 
  <Data Name="ProcessId">364</Data> 
  <Data Name="Image">C:\Windows\system32\svchost.exe</Data> 
  <Data Name="TargetObject">HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\test-task</Data> 
  <Data Name="User">NT AUTHORITY\SYSTEM</Data> 
  </EventData>
  </Event>

We configure the Wazuh agent to forward Sysmon logs to the manager. This is done by adding the following lines to the shared configuration file at /var/ossec/etc/shared/default/agent.conf on the Wazuh manager:

<agent_config os="windows">
  <localfile>
    <location>Microsoft-Windows-Sysmon/Operational</location>
    <log_format>eventchannel</log_format>
  </localfile>
</agent_config>

Next we create a rule to detect when a task has been scheduled. Another rule is also created to suppress events that are generated by the Windows update orchestrator service. The update orchestrator is responsible for downloading, installing, and verifying your computer updates. It constantly creates and deletes scheduled tasks, therefore generating a lot of alerts on the Wazuh dashboard which can become overwhelming for the analysts. The following rules are added to /var/ossec/etc/rules/local_rules.xml :

<group name="windows,sysmon,">
 
  <rule id="115006" level="6">
    <if_group>windows</if_group>
    <field name="win.eventdata.ruleName" type="pcre2">^technique_id=T1053,technique_name=Scheduled Task$</field>
    <field name="win.eventdata.eventType" type="pcre2">^CreateKey$</field>
    <description>A Newly Scheduled Task has been Detected on $(win.system.computer)</description>
    <mitre>
        <id>T1053</id>
    </mitre>
  </rule>

  <rule id="115007" level="0">
    <if_sid>115006</if_sid>
    <field name="win.eventdata.targetObject" type="pcre2">^HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Schedule\\\\TaskCache\\\\Tree\\\\Microsoft\\\\Windows\\\\UpdateOrchestrator$</field>
    <description>Suppression rule for scheduled task created by update orchestrator</description>
  </rule>

</group>

Creating the active response script

A batch script of the active response is written to do the following:

  • Receive active response logs from the Wazuh server
  • Extract the scheduled task name from the log
  • Query the task scheduler for the actions of the scheduled task
  • Finally, write the actions of the task to a log file.

The script is saved on the monitored endpoints at:

  • C:\Program Files (x86)\ossec-agent\active-response\bin\analyze-scheduled-task.cmd for 64 bit systems
  • C:\Program Files\ossec-agent\active-response\bin\analyze-scheduled-task.cmd for 32 bit systems.
@echo off

setlocal enableDelayedExpansion

reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && SET OS=32BIT || SET OS=64BIT


if %OS%==32BIT (
    SET logFile="%programfiles%\ossec-agent\logs\scheduled-tasks.log"
)

if %OS%==64BIT (
    SET logFile="%programfiles(x86)%\ossec-agent\logs\scheduled-tasks.log"
)

set input=
for /f "delims=" %%a in ('powershell -command "$logInput = Read-Host; Write-Output $logInput"') do (
    set input=%%a
)

powershell -command "$string = '%input%'; $match = select-string 'HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Schedule\\\\TaskCache\\\\Tree.*\\\\(\S*)\\r\\n' -inputobject $string; $taskName = $match.Matches.groups[1].value; $task = Get-ScheduledTask | where TaskName -EQ $taskName; $jsonTask = $task.Actions | ConvertTo-Json -Compress; try{$stream = [System.IO.StreamWriter]::new( '%logFile%', $true );'{\"ScheduledTaskAR\": ' + $jsonTask + ', \"TaskName\": \"' + $taskName + '\"}' | ForEach-Object{ $stream.WriteLine( $_ ) }}finally{$stream.close()}; exit"

Next, we enable active response in the wazuh manager configuration file at /var/ossec/etc/ossec.conf. The active response will trigger and execute the response analysis whenever there is a match for rule ID 115006:

<command>
   <name>analyze-scheduled-task</name>
   <executable>analyze-scheduled-task.cmd</executable>
   <timeout_allowed>no</timeout_allowed>
</command>

<active-response>
   <disabled>no</disabled>
   <command>analyze-scheduled-task</command>
   <location>local</location>
   <rules_id>115006</rules_id>
</active-response>

Restart the Wazuh manager to apply the changes:

systemctl restart wazuh-manager

Configure the Wazuh agent to monitor the active response log

Edit the shared configuration file at /var/ossec/etc/shared/default/agent.conf on the Wazuh manager and add the following lines to specify the path to the active response log:

<agent_config os="windows">
  <localfile>
    <location>logs\scheduled-tasks.log</location>
    <log_format>syslog</log_format>
  </localfile>
</agent_config>

In order to test the active response, we create a new scheduled task on the monitored endpoint. A new task can be scheduled by using the task scheduler GUI, or by running the following command on the monitored Windows endpoint with administrator privileges:

schtasks /create /tn test-task /tr "C:\Windows\System32\calc.exe" /sc onlogon /ru System /f

The following log sample is gotten in the active response log file (C:\Program Files (x86)\ossec-agent\logs\scheduled-tasks.log) proving that the script works:

{
  "ScheduledTaskAR": {
    "CimClass": {
      "CimSuperClassName": "MSFT_TaskAction",
      "CimSuperClass": {
        "CimSuperClassName": null,
        "CimSuperClass": null,
        "CimClassProperties": "Id",
        "CimClassQualifiers": "",
        "CimClassMethods": "",
        "CimSystemProperties": "Microsoft.Management.Infrastructure.CimSystemProperties"
      },
      "CimClassProperties": [
        "Id",
        "Arguments",
        "Execute",
        "WorkingDirectory"
      ],
      "CimClassQualifiers": [],
      "CimClassMethods": [],
      "CimSystemProperties": {
        "Namespace": "Root/Microsoft/Windows/TaskScheduler",
        "ServerName": "DESKTOP-4E0BQFT",
        "ClassName": "MSFT_TaskExecAction",
        "Path": null
      }
    },
    "CimInstanceProperties": [
      {
        "Name": "Id",
        "Value": null,
        "CimType": 14,
        "Flags": "Property, NotModified, NullValue",
        "IsValueModified": false
      },
      {
        "Name": "Arguments",
        "Value": null,
        "CimType": 14,
        "Flags": "Property, NotModified, NullValue",
        "IsValueModified": false
      },
      {
        "Name": "Execute",
        "Value": "C:\\Windows\\System32\\calc.exe",
        "CimType": 14,
        "Flags": "Property, NotModified",
        "IsValueModified": false
      },
      {
        "Name": "WorkingDirectory",
        "Value": null,
        "CimType": 14,
        "Flags": "Property, NotModified, NullValue",
        "IsValueModified": false
      }
    ],
    "CimSystemProperties": {
      "Namespace": "Root/Microsoft/Windows/TaskScheduler",
      "ServerName": "DESKTOP-4E0BQFT",
      "ClassName": "MSFT_TaskExecAction",
      "Path": null
    },
    "Id": null,
    "Arguments": null,
    "Execute": "C:\\Windows\\System32\\calc.exe",
    "WorkingDirectory": null,
    "PSComputerName": null
  },
  "TaskName": "test-task"
}

The active response script is written to log the actions of the scheduled task in JSON format. Wazuh incorporates an integrated decoder for JSON logs enabling the extraction of data from any source in this format, therefore we do not need to create custom decoders. We simply need to proceed to create rules that match and extract the relevant fields for analysis.

In order to clean up or delete the created task, we run the following command on the monitored endpoint:

schtasks /delete /tn test-task

Creating the rule

Next, we create a rule to trigger an alert with the decoded content from the logs:

<rule id="115008" level="10">
    <decoded_as>json</decoded_as>
    <field name="ScheduledTaskAR.Arguments" type="pcre2" negate="yes">^null$</field>
    <field name="TaskName" type="pcre2">(.|\s)*\S(.|\s)*</field>
    <description>A new scheduled task "$(TaskName)" has been created on "$(ScheduledTaskAR.CimSystemProperties.ServerName)". The task will execute the command - "$(ScheduledTaskAR.Execute) $(ScheduledTaskAR.Arguments)".</description>
    <mitre>
        <id>T1053</id>
    </mitre>
  </rule>
  
  <rule id="115009" level="10">
    <decoded_as>json</decoded_as>
    <field name="ScheduledTaskAR.Arguments" type="pcre2">^null$</field>
    <field name="TaskName" type="pcre2">(.|\s)*\S(.|\s)*</field>
    <description>A new scheduled task "$(TaskName)" has been created on "$(ScheduledTaskAR.CimSystemProperties.ServerName)". The task will execute the command - "$(ScheduledTaskAR.Execute)".</description>
    <mitre>
        <id>T1053</id>
    </mitre>
  </rule>

Restart the Wazuh manager to apply the changes:

systemctl restart wazuh-manager

In order to test the rule, we created a scheduled task on the Windows endpoint. The active response script is triggered and we can see the content of the scheduled task on the Wazuh dashboard.

We can see the content of the scheduled task on the Wazuh dashboard

NOTE: Ensure to clean the endpoint by deleting the scheduled task that was created.

We have been able to create an active response that extracts the relevant information of any new scheduled task and present it on the Wazuh dashboard for the analyst. This eliminates the need for the security analyst to manually connect to the endpoint for further investigation when a task is scheduled. An analyst can easily see when an unusual script, application, or command is being scheduled and proceed to act on it. This can speed up the detection of some recent malware such as the Tarrask malware that utilizes the task scheduler for persistence.