Many behaviors are more complex and are best described with an ordered
sequence of multiple events over a short interval.
Complex behaviors may share properties between events in the sequence or require careful handling of state.
- Core sequence template
sequence [event_type1 where condition1] [event_type2 where condition2] ... [event_typeN where conditionN]
An example of simple behavior that can spans multiple events is a network logon over Remote Desktop.
maxspan of 30 seconds, we would expect to see an incoming network connection from a host,
followed by a separate event for the remote authentication success or failure.
sequence with maxspan=30s [network where destination_port==3389 and event_subtype_full="*_accept_event*"] [security where event_id in (4624, 4625) and logon_type == 10]
sequence connects the two events temporally, it doesn’t prove that they are related.
There could be incoming attempts over Remote Desktop from multiple computers, leading to more
network and security events. The sequence can be constrained
by matching fields,
so that the network connection and the logon event must share the same source host.
sequence with maxspan=30s [network where destination_port==3389 and event_subtype_full="*_accept_event"] by source_address [security where event_id in (4624, 4625) and logon_type == 10] by ip_address
For some sequences, multiple values need to be shared across the sequence. One example for this is a user that creates a file and shortly executes it.
sequence with maxspan=5m [ file where file_name == "*.exe"] by user_name, file_path [ process where true] by user_name, process_path
Since some fields are in common across all events, this could be represented more succinctly
by user_name to the top of the query.
sequence by user_name with maxspan=5m [ file where file_name == "*.exe"] by file_path [ process where true] by process_path
sequence needs to carefully manage and expire state. Sequences are valid
until a specific event occurs. This can help expire non-unique identifiers and reduce memory usage.
Handles and process identifiers are frequently reused. Stateful
sequence tracking avoids invalid pairs of events.
Within Windows, a process identifier (PID) is only unique while a process is running, but can be reused after its termination.
When building a
sequence of process identifiers, a process termination will cause all state to be invalidated and thrown away.
For instance, if
whoami.exe executed from a batch file, matching ppid of
whoami.exe to the pid of
can only be done while the parent process is alive. As a result, the sequence is valid
the matching termination event occurs.
sequence [ process where process_name == "cmd.exe" and command_line == "* *.bat*" and event_subtype_full == "creation_event"] by pid [ process where process_name == "whoami.exe" and event_subtype_full == "creation_event"] by ppid until [ process where event_subtype_full == "termination_event"] by pid