This artifact will extract Event Logs related to ScheduledTasks and provide a nice format for simplified review.
Adversaries may abuse tasks for execution, persistence, lateral movement or privilege escalation. This artifact collates all events from Microsoft-Windows-TaskScheduler/Operational event log channel and scheduled task events from the Security log if configured.
A common hunting use case may be collection all deleted scheduled tasks (EID 141), all modified scheduled tasks (EID 140) then run frequency analysis and chase down any abnormalities for the environment. Similarly task execution (EID 129) and registration (EID 106) can be a good collection hunting for unusual paths.
Pivoting can be via either: TaskSchedulerEventRegex, TaskName or IOC Regex (e.g taskname|delete|created|update)
Note: Audit Other Object Access Events is required to be implemented to record scheduled tasks being registered, modified or disabled in the Security event log channel. See: Computer Configuration\Policies\Windows Settings\Security Settings\Advanced Audit Policy Configuration\Object Access
name: Windows.EventLogs.ScheduledTasks
description: |
This artifact will extract Event Logs related to ScheduledTasks and provide
a nice format for simplified review.
Adversaries may abuse tasks for execution, persistence, lateral movement or
privilege escalation. This artifact collates all events from
Microsoft-Windows-TaskScheduler/Operational event log channel and scheduled
task events from the Security log if configured.
A common hunting use case may be collection all deleted scheduled tasks (EID 141),
all modified scheduled tasks (EID 140) then run frequency analysis and chase
down any abnormalities for the environment. Similarly task execution (EID 129)
and registration (EID 106) can be a good collection hunting for unusual paths.
Pivoting can be via either: TaskSchedulerEventRegex, TaskName or IOC Regex
(e.g taskname|delete|created|update)
Note: Audit Other Object Access Events is required to be implemented to record
scheduled tasks being registered, modified or disabled in the Security event
log channel.
See: Computer Configuration\Policies\Windows Settings\Security Settings\Advanced Audit Policy Configuration\Object Access
author: "@mgreen27 - Matt Green"
precondition: SELECT OS From info() where OS = 'windows'
reference:
- https://attack.mitre.org/techniques/T1053/005/
- https://mnaoumov.wordpress.com/2014/05/15/task-scheduler-event-ids/
parameters:
- name: Security
description: path to Security event log.
default: '%SystemRoot%\System32\Winevt\Logs\Security.evtx'
- name: TaskScheduler
description: path to the TaskScheduler/Operational event log
default: '%SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TaskScheduler%4Operational.evtx'
- name: DateAfter
description: "search for events after this date. YYYY-MM-DDTmm:hh:ss Z"
type: timestamp
- name: DateBefore
description: "search for events before this date. YYYY-MM-DDTmm:hh:ss Z"
type: timestamp
- name: TaskSchedulerEventRegex
description: Regex of TaskScheduler log event ids.
type: regex
default: .
- name: SecurityEventRegex
description: regex of Security log event ids.
type: regex
default: '^(4698|4699|4700|4701|4702)$'
- name: TaskNameRegex
description: regex of target task name.
default: .
type: regex
- name: TaskNameWhitelist
description: regex of task names to exclude from results.
default:
type: regex
- name: TaskActionRegex
description: regex of target task execution process / path.
default: .
type: regex
- name: TaskActionWhitelist
description: regex of task processes to exclude from results.
default:
type: regex
- name: UserNameRegex
description: regex of target user name.
default: .
type: regex
- name: IocRegex
description: IOC regex to search for.
default: .
type: regex
- name: VSSAnalysisAge
type: int
default: 0
description: |
If larger than zero we analyze VSS within this many days
ago. (e.g 7 will analyze all VSS within the last week). Note
that when using VSS analysis we have to use the ntfs accessor
for everything which will be much slower.
sources:
- query: |
LET VSS_MAX_AGE_DAYS <= VSSAnalysisAge
LET Accessor = if(condition=VSSAnalysisAge > 0, then="ntfs_vss", else="auto")
-- firstly set timebounds for performance
LET DateAfterTime <= if(condition=DateAfter,
then=DateAfter, else=timestamp(epoch="1600-01-01"))
LET DateBeforeTime <= if(condition=DateBefore,
then=DateBefore, else=timestamp(epoch="2200-01-01"))
-- Lookup what each task ID means (sadly dict keys are always strings).
LET TaskIDLookup <= dict(
`4698`="A scheduled task was created.",
`4699`="A scheduled task was deleted.",
`4700`="A scheduled task was enabled.",
`4701`="A scheduled task was disabled.",
`4702`="A scheduled task was updated.")
-- expand provided glob into a list of paths on the file system (fs)
LET fspaths = SELECT OSPath
FROM glob(globs=[expand(path=Security), expand(path=TaskScheduler)],
accessor=Accessor)
-- function to parse task content and replace xml in EventData
LET parse_task(data) = dict(
SubjectUserSid=data.SubjectUserSid,
SubjectUserName=data.SubjectUserName,
SubjectDomainName=data.SubjectDomainName,
SubjectLogonId=data.SubjectLogonId,
TaskName=data.TaskName,
TaskContent=parse_xml(
accessor='data',
file=regex_replace(
source= if(condition= data.TaskContentNew,
then= data.TaskContentNew,
else= if(condition= data.TaskContent,
then= data.TaskContent)),
re='<[?].+?>',
replace='')).Task,
ClientProcessStartKey=data.ClientProcessStartKey,
ClientProcessId=data.ClientProcessId,
ParentProcessId=data.ParentProcessId,
RpcCallClientLocality=data.RpcCallClientLocality,
FQDN=data.FQDN)
-- function returning query hits
LET evtxsearch(PathList) = SELECT * FROM foreach(
row=PathList,
query={
SELECT
timestamp(epoch=int(int=System.TimeCreated.SystemTime)) AS EventTime,
System.Computer as Computer,
System.Channel as Channel,
System.EventID.Value as EventID,
System.EventRecordID as EventRecordID,
if(condition=EventData.UserName,
then= EventData.UserName,
else= if(condition=EventData.UserContext,
then=EventData.UserContext,
else= if(condition=EventData.SubjectUserName,
then= EventData.SubjectDomainName + '\\' + EventData.SubjectUserName,
else= 'N/A' ))) as UserName,
if(condition=EventData.TaskName,
then= EventData.TaskName) as TaskName,
if(condition=EventData.ActionName,
then= EventData.ActionName,
else= if(condition=EventData.Path,
then= EventData.Path,
else= 'N/A' )) as TaskAction,
if(condition=EventData.TaskContent OR EventData.TaskContentNew,
then= parse_task(data=EventData),
else= EventData) as EventData,
get(field="Message") as Message,
OSPath
FROM parse_evtx(filename=OSPath, accessor=Accessor)
WHERE
(( Channel = 'Microsoft-Windows-TaskScheduler/Operational'
AND str(str=EventID) =~ TaskSchedulerEventRegex )
OR ( Channel = 'Security'
AND str(str=EventID) =~ SecurityEventRegex ))
AND TaskName =~ TaskNameRegex
AND NOT if(condition= TaskNameWhitelist,
then= TaskName =~ TaskNameWhitelist,
else= False)
AND TaskAction =~ TaskActionRegex
AND NOT if(condition= TaskActionWhitelist,
then= TaskName =~ TaskActionWhitelist,
else= False)
AND UserName =~ UserNameRegex
AND format(format='%v %v %v %v', args=[
EventData, UserData, Message, System]) =~ IocRegex
AND EventTime >= DateAfterTime AND EventTime <= DateBeforeTime
}
)
SELECT
EventTime,
Computer,
Channel,
EventID,
EventRecordID,
UserName,
TaskName,
if(condition= Channel = 'Microsoft-Windows-TaskScheduler/Operational',
then= Message,
else=get(item=TaskIDLookup, member=str(str=EventID))) as Message,
if(condition= EventID =~'^(4698|4699|4700|4701|4702)$',
then= if(condition= EventData.TaskContent.Actions,
then= if(condition= EventData.TaskContent.Actions.Exec,
then= if(condition= EventData.TaskContent.Actions.Exec.Arguments,
then= EventData.TaskContent.Actions.Exec.Command + ' ' + EventData.TaskContent.Actions.Exec.Arguments,
else= EventData.TaskContent.Actions.Exec.Command),
else= if(condition=EventData.TaskContent.Actions.ComHandler.ClassId,
then= 'ClassId: ' + EventData.TaskContent.Actions.ComHandler.ClassId))),
else= TaskAction) as TaskAction,
EventData,
OSPath
FROM evtxsearch(PathList=fspaths)