Skip to content

Commit deff52e

Browse files
committed
fix(rules_engine): Collect matches for unconstrained sequences
If the sequence is unconstrained and the partials don't have sequence links, we can collect the first matching event of each sequence slot.
1 parent 1181763 commit deff52e

2 files changed

Lines changed: 95 additions & 3 deletions

File tree

pkg/rules/sequence.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,9 @@ func (s *sequenceState) runSequence(e *event.Event) bool {
505505

506506
// if both the terminal state is reached and the partials
507507
// in the sequence state could be joined by the specified
508-
// field(s), the rule has matched successfully, and we can
509-
// collect all events involved in the rule match
508+
// field(s) or the sequence is unconstrained, the rule has
509+
// matched successfully, and we can collect all events involved
510+
// in the rule match
510511
isTerminal := s.isTerminalState()
511512
if isTerminal {
512513
setMatch := func(seqID int, e *event.Event) {
@@ -521,7 +522,11 @@ func (s *sequenceState) runSequence(e *event.Event) bool {
521522
for seqID := 0; seqID < len(s.partials); seqID++ {
522523
for _, outer := range s.partials[seqID] {
523524
for _, inner := range s.partials[seqID+1] {
524-
if filter.CompareSeqLinks(outer.SequenceLinks(), inner.SequenceLinks()) {
525+
switch {
526+
case filter.CompareSeqLinks(outer.SequenceLinks(), inner.SequenceLinks()):
527+
setMatch(seqID, outer)
528+
setMatch(seqID+1, inner)
529+
case !s.seq.IsConstrained() && !outer.ContainsMeta(event.RuleSequenceLinks) && !inner.ContainsMeta(event.RuleSequenceLinks):
525530
setMatch(seqID, outer)
526531
setMatch(seqID+1, inner)
527532
}

pkg/rules/sequence_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,93 @@ func TestSimpleSequenceMultiplePartials(t *testing.T) {
373373
assert.Equal(t, "C:\\Temp\\file.tmp", ss.matches[1].GetParamAsString(params.FilePath))
374374
}
375375

376+
func TestUnconstrainedSequenceMatches(t *testing.T) {
377+
log.SetLevel(log.DebugLevel)
378+
379+
c := &config.FilterConfig{Name: "Command shell created a temp file"}
380+
f := filter.New(`
381+
sequence
382+
maxspan 200ms
383+
|evt.name = 'CreateProcess' and ps.name = 'cmd.exe'|
384+
|evt.name = 'CreateFile' and file.path icontains 'temp'|
385+
`, &config.Config{EventSource: config.EventSourceConfig{EnableFileIOEvents: true}, Filters: &config.Filters{}})
386+
require.NoError(t, f.Compile())
387+
388+
ss := newSequenceState(f, c, new(ps.SnapshotterMock))
389+
390+
e1 := &event.Event{
391+
Seq: 20,
392+
Type: event.CreateProcess,
393+
Timestamp: time.Now().Add(time.Second),
394+
Name: "CreateProcess",
395+
Tid: 2484,
396+
PID: 859,
397+
PS: &pstypes.PS{
398+
Name: "cmd.exe",
399+
Exe: "C:\\Windows\\System32\\cmd.exe",
400+
PID: 859,
401+
Parent: &pstypes.PS{
402+
Name: "WmiPrvSE.exe",
403+
},
404+
},
405+
Params: event.Params{
406+
params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(859)},
407+
},
408+
Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"},
409+
}
410+
e2 := &event.Event{
411+
Seq: 21,
412+
Type: event.CreateProcess,
413+
Timestamp: time.Now().Add(time.Second),
414+
Name: "CreateProcess",
415+
Tid: 2484,
416+
PID: 1859,
417+
PS: &pstypes.PS{
418+
Name: "cmd.exe",
419+
Exe: "C:\\Windows\\System32\\cmd.exe",
420+
PID: 1859,
421+
Parent: &pstypes.PS{
422+
Name: "svchost.exe",
423+
},
424+
},
425+
Params: event.Params{
426+
params.ProcessID: {Name: params.ProcessID, Type: params.PID, Value: uint32(859)},
427+
},
428+
Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"},
429+
}
430+
e3 := &event.Event{
431+
Type: event.CreateFile,
432+
Seq: 25,
433+
Timestamp: time.Now().Add(time.Second * time.Duration(2)),
434+
Name: "CreateFile",
435+
Tid: 2484,
436+
PID: 3859,
437+
Category: event.File,
438+
PS: &pstypes.PS{
439+
Name: "cmd.exe",
440+
Exe: "C:\\Windows\\system32\\cmd.exe",
441+
PID: 3859,
442+
},
443+
Params: event.Params{
444+
params.FilePath: {Name: params.FilePath, Type: params.UnicodeString, Value: "C:\\Temp\\file.tmp"},
445+
},
446+
Metadata: map[event.MetadataKey]any{"foo": "bar", "fooz": "barzz"},
447+
}
448+
449+
require.False(t, ss.runSequence(e1))
450+
require.False(t, ss.runSequence(e2))
451+
assert.Len(t, ss.partials[0], 2)
452+
assert.Len(t, ss.partials[1], 0)
453+
require.True(t, ss.runSequence(e3))
454+
assert.Len(t, ss.partials[1], 1)
455+
456+
require.Len(t, ss.matches, 2)
457+
assert.Equal(t, uint32(859), ss.matches[0].PID)
458+
assert.Equal(t, "WmiPrvSE.exe", ss.matches[0].PS.Parent.Name)
459+
assert.Equal(t, uint32(3859), ss.matches[1].PID)
460+
assert.Equal(t, "C:\\Temp\\file.tmp", ss.matches[1].GetParamAsString(params.FilePath))
461+
}
462+
376463
func TestSimpleSequenceDeadline(t *testing.T) {
377464
log.SetLevel(log.DebugLevel)
378465

0 commit comments

Comments
 (0)