Hunting for the Behavior: Scheduled Tasks

In my previous post, I explained how to generate process trees originating from specific processes and find unusual patterns. How about if we could generate process trees and get extra information about these process trees so that we can quickly detect suspicious/malicious activity? Let’s have a look at one of the most common techniques: Scheduled Tasks.

Adversaries create scheduled tasks to achieve persistence. You can detect suspicious scheduled task creations if it’s created in a normal/common. However, if the scheduled task is created by using Windows API or via a misconfigured GPO etc., it might be difficult and time consuming to detect every single procedure. In fact, this applies to any technique that can be executed via several procedures. Therefore, instead of trying to detect every single procedure of a technique, we should focus on the behavior as much as possible.

Coming back to Scheduled Tasks, one of the goals is to keep the C2 channel active and I think this is the most common goal. Another goal might be to execute a one-time job on a machine indirectly, but let’s focus on the C2 channel.

If the goal is to keep the C2 channel active, there must be a network connection originating from a scheduled task. A scheduled task, which spawns a process that spawns another process…, and the last process connects to the C2 server as shown below:

Well, there might be other network connections made by previous processes as well; who knows? By getting this contextual information about the processes originating from a scheduled task, we can detect malicious activity with high precision and a low false-positive rate.

How to get contextual information about the processes?

We already know how to generate the process tree and find unusual patterns. So, we can already detect the one-time execution scenario. The tricky part of linking a scheduled task to a network communication for C2 detection is getting all network connections of every single process in each tree. After racking my brain, I’ve found a way to pivot from all processes to their network connections by using KQL(you can do the same with Sysmon logs by using GUID information). The KQL query uses the pack_array function to pack process information, then duplicates each process tree by using the mv-expand. Finally, the query joins the table with the DeviceNetworkEvents. The snippet of the query:

The logic here can be used for other tactics/techniques as well. We just need to know the main goal of the technique. Also, the method can be implemented with Sysmon logs in Azure Sentinel, Splunk and probably in Jupyter Notebook too.

You can find the whole query in my GitHub repo. Also, you can now follow me on Twitter as I’ll start posting tips&tricks related to threat hunting & detection, and security in general.

Cyber Defense Professional. @Cyb3rMonk ( Threat Hunting | Active Defense | Cyber Deception | SOC | SIEM )

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store