Monitoring with PowerShell Chapter 3: Monitoring creation of scheduled tasks

Hi All,

After a blog post from Malwarebytes (here) about specific adware and cryptolockers using scheduled tasks to make sure they can remain undetected, or even regain control of the system by running a specific task every once in a while, We’ve decided with to start monitoring the creation of scheduled tasks. Users generally don’t really setup these tasks in normal situations.

We’ve decided to start alerting on any task created in the last 24 hours. Our N-Central monitoring system creates a ticket for this so we can investigate it. The great trick about this is that the set automatically resets itself after 24 hours, unless we create another task.

The monitoring set

Ok, so lets get started! the ingredients we will need today are:

  • Windows 8.1 or higher
  • PowerShell
  • A monitoring system, or way to run the scripts.

First, We’ll start by trying to get all tasks on our system. We’ll use the get-scheduledtask cmdlet.

Get-ScheduledTask

Now we have a cool list of tasks, but hey, there is no date information that is returned by this cmdlet?! That’s no fun. It seems like Microsoft had a little oversight in this case. Still, its not really a problem for us; Microsoft always stores the XML files for the scheduled tasks on the same location, namely %WINDIR%\System32\Tasks.

Instead of messing with extra modules, or even trying to get date information out of the default cmdlets we’ll move on to using get-childitem, with a filter of 24 hours. Lets start by grabbing our Task Path and setting it to a variable for ease of use, and then get the list of tasks created today.

$Taskpath = "$ENV:windir\system32\Tasks"
$TasksToday = Get-ChildItem -Path $TaskPath -Recurse| Where-Object {$_.CreationTime -gt (Get-Date).AddDays(-1)}

Ok, Great! now we already have a list of all files created today. We could stop right now, set this up in our monitoring system and be done with it. The only issue I have with this is that I’d like to know the actual tasks settings. So lets try getting those too.

We start looping through all the files, loading them as XML objects, and getting the information we care about. in our case we only want to know the name, author, and what command it would execute. We put that information in $TaskState. If $Taskstate remains blank during the entire script. We simply output “Healthy”.

$Taskpath = "$ENV:windir\system32\Tasks"
$TasksToday = Get-ChildItem -Path $TaskPath -Recurse| Where-Object {$_.CreationTime -gt (Get-Date).AddDays(-1)}

Foreach($task in $TasksToday){
$TaskXML = get-content $Task.FullName
$TaskName = $($TaskXML.task.RegistrationInfo.uri)
$TaskAuthor = $TaskXML.task.Principals.Principal.userid
$TaskExec = $TaskXML.task.actions.exec.command + $TaskXML.task.actions.exec.Arguments
$TaskState += "$TaskName has been created by SID: $TaskAuthor and executes $TaskExec`n"
}

if(!$TaskState){ $TaskState = "Healthy"}

Based on this information, we load this into our monitoring system and alert if the TaskState is anything but “Healthy”. And that’s it, we’re now monitoring the scheduled tasks with PowerShell. Happy PowerShelling!

Update: it was reported by a reader that he has some jobs that he did not have permissions on, and thus the task failed. My RMM system runs every PowerShell command as SYSTEM, so this wasn’t noticed during testing. Anyway, to solve this issue you can run the script below instead. This script forces ownership to be taken over for the current account.

$Taskpath = "$ENV:windir\system32\Tasks"
takeown /a /r /d Y /f "$TaskPath"
$TasksToday = Get-ChildItem -Path $TaskPath -Recurse| Where-Object {$_.CreationTime -gt (Get-Date).AddDays(-1)}

Foreach($task in $TasksToday){
$TaskXML = get-content $Task.FullName
$TaskName = $($TaskXML.task.RegistrationInfo.uri)
$TaskAuthor = $TaskXML.task.Principals.Principal.userid
$TaskExec = $TaskXML.task.actions.exec.command + $TaskXML.task.actions.exec.Arguments
$TaskState += "$TaskName has been created by SID: $TaskAuthor and executes $TaskExec`n"
}

if(!$TaskState){ $TaskState = "Healthy"}

8 Comments

  1. Sasa Zelic April 20, 2020 at 8:18 pm

    Great job !

  2. ROBERTO TASSO October 13, 2020 at 2:20 pm

    hello,

    i use your component, very useful; sometime create alert with no description, like this:

    Monitor Scheduled Task Creation [WIN] V3 – Tasks: has been created by SID: and executes

    can you help me for workaround? Thanks

    1. Kelvin Tegelaar October 13, 2020 at 3:11 pm

      Often these are files created in the path. I’d suggest to look into the path by checking out what file was created.

  3. Toby December 15, 2020 at 7:48 am

    Have you had issues with certain windows 10 scheduled tasks been frequently adjusted by the system itself?

    1. Kelvin Tegelaar December 15, 2020 at 9:38 am

      No I haven’t seen that at all, do you have a name of the task? Maybe I can help you find what’s going on.

      1. Toby December 18, 2020 at 12:37 pm

        We had a lot of alerts for a system task “Update Orchestrator Start” task, annoyingly I’ve since removed the policy from DRMM and the tickets it raised. Am re-deploying to see what we get back from a test group of devices. Will keep you posted…

        1. Toby December 18, 2020 at 3:43 pm

          Found it! I was wrong on the name

          Alert: \Microsoft\Windows\UpdateOrchestrator\Universal Orchestrator Start has been created by SID: S-1-5-18 and executes %systemroot%\system32\usoclient.exeStartUWork

          Some output from the diagnostics:
          Directory: C:\WINDOWS\system32\Tasks\Microsoft\Windows\UpdateOrchestrator

          Mode LastWriteTime Length Name
          —- ————- —— —-
          -a—- 18/12/2020 14:26 3394 Universal Orchestrator Start

  4. Pingback: Breaking Cyber Attack Chains With 5 Tools You Already Have Access To - ChannelE2E

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.