The Scheduled Task Was Created At 2:41AM
Persistence does not always arrive as ransomware.
It does not always arrive as a suspicious executable sitting on the desktop.
Sometimes it arrives as something Windows administrators use every day: a Scheduled Task.
At 2:41AM, a new task appeared. It looked ordinary. It used a legitimate Windows feature. It did not scream incident.
But every reboot gave the attacker another chance to come back.
Scheduled Task Persistence
A PowerShell process ran. A payload was downloaded. Then a Scheduled Task was created. Individually, each event looked explainable. Together, they told the persistence story.
The clue at 2:41AM
Why attackers use Scheduled Tasks
Find schtasks.exe executions
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "schtasks.exe"
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName,
InitiatingProcessCommandLine, FileName, ProcessCommandLine
| order by Timestamp desc
Hunt for suspicious task creation commands
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has_any ("/create", "/change", "/sc", "/tn", "/tr")
| where ProcessCommandLine has_any ("powershell", "cmd.exe", "wscript", "cscript", "mshta", "AppData", "Temp", "ProgramData")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
Review Scheduled Task registry evidence
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
DeviceRegistryEvents
| where Timestamp > ago(14d)
| where RegistryKey has @"Schedule\TaskCache"
| project Timestamp, DeviceName, ActionType, RegistryKey,
RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
Build the persistence timeline
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
let InvestigationDevice = "DEVICE-NAME-HERE";
let StartTime = ago(2d);
union isfuzzy=true
(
DeviceProcessEvents
| where Timestamp > StartTime
| where DeviceName =~ InvestigationDevice
| where FileName in~ ("powershell.exe", "cmd.exe", "schtasks.exe")
| project Timestamp, DeviceName, EvidenceType="Process", ActionType,
Detail=ProcessCommandLine
),
(
DeviceNetworkEvents
| where Timestamp > StartTime
| where DeviceName =~ InvestigationDevice
| project Timestamp, DeviceName, EvidenceType="Network", ActionType,
Detail=strcat(RemoteUrl, " ", RemoteIP, ":", RemotePort)
),
(
DeviceFileEvents
| where Timestamp > StartTime
| where DeviceName =~ InvestigationDevice
| project Timestamp, DeviceName, EvidenceType="File", ActionType,
Detail=strcat(FolderPath, "\", FileName)
),
(
DeviceRegistryEvents
| where Timestamp > StartTime
| where DeviceName =~ InvestigationDevice
| where RegistryKey has @"Schedule\TaskCache"
| project Timestamp, DeviceName, EvidenceType="Registry", ActionType,
Detail=strcat(RegistryKey, " ", RegistryValueData)
)
| order by Timestamp asc
Look for Defender scheduled task events
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
DeviceEvents
| where Timestamp > ago(14d)
| where ActionType has_any ("ScheduledTask", "Task")
| project Timestamp, DeviceName, ActionType, AdditionalFields,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
