This artifact will compare EventLog records and report on abnormalities in RecordID sequence and optional time gap. The artifact can be used for both hunting, remote or local analysis.
There are several parameter’s available.
Note: Please use with caution this artifact can potentially be heavy on the endpoint. Temporal analysis is turned off by default due to potential for false positives during machine shutdown. Sequential false positives may also occur very occasionally.
version: 0.6.1
name: Windows.EventLogs.RecordIDCheck
author: Matt Green - @mgreen27
description: |
This artifact will compare EventLog records and report on
abnormalities in RecordID sequence and optional time gap. The
artifact can be used for both hunting, remote or local analysis.
There are several parameter's available.
- EvtxGlob glob of EventLogs to target. Default to all but can be targeted.
- PathRegex enables filtering on evtx path for specific log targetting.
- DateAfter enables search for events after this date.
- DateBefore enables search for events before this date.
- MaxTimeDifference enables flaging temporal gaps between Events. Note also potential false positives on machines turned off.
- SearchVSS enables searching over VSS
Note: Please use with caution this artifact can potentially be heavy
on the endpoint. Temporal analysis is turned off by default due to
potential for false positives during machine shutdown. Sequential
false positives may also occur very occasionally.
version: 0.6.1
parameters:
- name: EvtxGlob
description: Target glob to process for abnormalities.
default: '%SystemRoot%\System32\Winevt\Logs\*.evtx'
- name: PathRegex
description: Event log Regex to enable filtering on path
default: .
- name: DateAfter
type: timestamp
description: "search for events after this date. YYYY-MM-DDTmm:hh:ssZ"
- name: DateBefore
type: timestamp
description: "search for events before this date. YYYY-MM-DDTmm:hh:ssZ"
- name: MaxTimeDifference
description: Alert on events with a gap between previous event greater than this number in seconds.
- name: SearchVSS
description: "Add VSS into query."
type: bool
sources:
- query: |
-- time testing
LET time_test(stamp) =
if(condition= DateBefore AND DateAfter,
then= stamp < DateBefore AND stamp > DateAfter,
else=
if(condition=DateBefore,
then= stamp < DateBefore,
else=
if(condition= DateAfter,
then= stamp > DateAfter,
else= True
)))
-- create dict for previous results.
LET EvtxPath<=dict(FullPath='',RecordID='',EventTime='')
-- expand provided glob into a list of paths on the file system (fs)
LET fspaths <= SELECT FullPath
FROM glob(globs=expand(path=EvtxGlob))
WHERE FullPath =~ PathRegex
-- function returning list of VSS paths corresponding to path
LET vsspaths(path) = SELECT FullPath
FROM Artifact.Windows.Search.VSS(SearchFilesGlob=path)
WHERE FullPath =~ PathRegex
-- function returning IOC hits
LET evtxsearch(PathList) = SELECT * FROM foreach(
row=PathList,
query={
SELECT
FullPath,
System.Computer as Computer,
System.Channel as Channel,
EvtxPath.OLDEventTime as FirstEventTime,
set(item=EvtxPath,field='OLDFullPath',value=EvtxPath.FullPath) as _SetOLDFullPath,
set(item=EvtxPath,field='FullPath',value=FullPath) as _SetFullPath,
set(item=EvtxPath,field='OLDRecordID',value=EvtxPath.RecordID) as _SetOLDRecordID,
set(item=EvtxPath,field='RecordID',value=System.EventRecordID) as _SetRecordID,
set(item=EvtxPath,field='OLDEventTime',value=EvtxPath.EventTime) as _SetOLDEventTime,
set(item=EvtxPath,field='EventTime',value=timestamp(epoch=int(int=System.TimeCreated.SystemTime))) as _SetEventTime,
EvtxPath.OLDRecordID as FirstRecordID,
timestamp(epoch=int(int=System.TimeCreated.SystemTime)) AS SecondEventTime,
System.EventRecordID as SecondRecordID,
System.EventRecordID - EvtxPath.OLDRecordID as _RecordIDSequence,
EvtxPath.OLDFullPath as _OLDFullPath
FROM parse_evtx(filename=FullPath)
WHERE
time_test(stamp=SecondEventTime)
}
)
-- include VSS
LET include_vss = SELECT * FROM foreach(row=fspaths,
query={
SELECT *
FROM evtxsearch(PathList={
SELECT FullPath FROM vsspaths(path=FullPath)
})
--GROUP BY EventRecordID,Channel
})
-- exclude VSS`
LET exclude_vss = SELECT *
FROM evtxsearch(PathList={SELECT FullPath FROM fspaths})
-- return rows
SELECT *,
SecondEventTime.Unix - FirstEventTime.Unix as SecondsGap,
if(condition= NOT _RecordIDSequence=1,
then= "EventRecordID not sequential",
else= "Gap between EventRecordIDs exceeds maximum seconds.") as Description
FROM if(condition=SearchVSS,
then=include_vss,
else=exclude_vss)
WHERE _RecordIDSequence
AND FullPath = _OLDFullPath
AND
( if(condition=MaxTimeDifference,
then= SecondsGap > int(int=MaxTimeDifference),
else= False)
OR NOT _RecordIDSequence=1 )