@@ -127,6 +127,248 @@ https://en.wikipedia.org/wiki/Unix_time[Unix epoch], in ascending order.
127
127
// TESTRESPONSE[s/"took": 60/"took": $body.took/]
128
128
====
129
129
130
+ [discrete]
131
+ [[eql-search-sequence]]
132
+ === Search for a sequence of events
133
+
134
+ Many query languages allow you to match single events. However, EQL's
135
+ <<eql-sequences,sequence syntax>> lets you match an ordered series of events.
136
+
137
+ .*Example*
138
+ [%collapsible]
139
+ ====
140
+ The following EQL search request matches a sequence that:
141
+
142
+ . Starts with an event with:
143
+ +
144
+ --
145
+ * An `event.category` of `file`
146
+ * A `file.name` of `cmd.exe`
147
+ --
148
+ . Followed by an event with:
149
+ +
150
+ --
151
+ * An `event.category` of `process`
152
+ * A `process.name` that contains the substring `regsvr32`
153
+ --
154
+
155
+ [source,console]
156
+ ----
157
+ GET /sec_logs/_eql/search
158
+ {
159
+ "query": """
160
+ sequence
161
+ [ file where file.name == "cmd.exe" ]
162
+ [ process where stringContains(process.name, "regsvr32") ]
163
+ """
164
+ }
165
+ ----
166
+
167
+ The API returns the following response. Matching events in
168
+ the `hits.sequences.events` property are sorted by
169
+ <<eql-search-api-timestamp-field,timestamp>>, converted to milliseconds since
170
+ the https://en.wikipedia.org/wiki/Unix_time[Unix epoch], in ascending order.
171
+
172
+ [source,console-result]
173
+ ----
174
+ {
175
+ "took": 60,
176
+ "timed_out": false,
177
+ "hits": {
178
+ "total": {
179
+ "value": 1,
180
+ "relation": "eq"
181
+ },
182
+ "sequences": [
183
+ {
184
+ "events": [
185
+ {
186
+ "_index": "sec_logs",
187
+ "_id": "4",
188
+ "_score": null,
189
+ "_source": {
190
+ "@timestamp": "2020-12-07T11:07:08.000Z",
191
+ "agent": {
192
+ "id": "8a4f500d"
193
+ },
194
+ "event": {
195
+ "category": "file"
196
+ },
197
+ "file": {
198
+ "accessed": "2020-12-07T11:07:08.000Z",
199
+ "name": "cmd.exe",
200
+ "path": "C:\\Windows\\System32\\cmd.exe",
201
+ "type": "file",
202
+ "size": 16384
203
+ },
204
+ "process": {
205
+ "name": "cmd.exe",
206
+ "path": "C:\\Windows\\System32\\cmd.exe"
207
+ }
208
+ },
209
+ "fields": {
210
+ "@timestamp": [
211
+ "1607339228000"
212
+ ]
213
+ },
214
+ "sort": [
215
+ 1607339228000
216
+ ]
217
+ },
218
+ {
219
+ "_index": "sec_logs",
220
+ "_id": "5",
221
+ "_score": null,
222
+ "_source": {
223
+ "@timestamp": "2020-12-07T11:07:09.000Z",
224
+ "agent": {
225
+ "id": "8a4f500d"
226
+ },
227
+ "event": {
228
+ "category": "process"
229
+ },
230
+ "process": {
231
+ "name": "regsvr32.exe",
232
+ "path": "C:\\Windows\\System32\\regsvr32.exe"
233
+ }
234
+ },
235
+ "fields": {
236
+ "@timestamp": [
237
+ "1607339229000"
238
+ ]
239
+ },
240
+ "sort": [
241
+ 1607339229000
242
+ ]
243
+ }
244
+ ]
245
+ }
246
+ ]
247
+ }
248
+ }
249
+ ----
250
+ // TESTRESPONSE[s/"took": 60/"took": $body.took/]
251
+
252
+ You can further constrain matching event sequences using the `by` keyword.
253
+
254
+ The following EQL search request adds `by agent.id` to each event item. This
255
+ ensures events matching the sequence share the same `agent.id` field value.
256
+
257
+ [source,console]
258
+ ----
259
+ GET /sec_logs/_eql/search
260
+ {
261
+ "query": """
262
+ sequence
263
+ [ file where file.name == "cmd.exe" ] by agent.id
264
+ [ process where stringContains(process.name, "regsvr32") ] by agent.id
265
+ """
266
+ }
267
+ ----
268
+
269
+ Because the `agent.id` field is shared across all events in the sequence, it
270
+ can be included using `sequence by`. The following query is equivalent to the
271
+ prior one.
272
+
273
+ [source,console]
274
+ ----
275
+ GET /sec_logs/_eql/search
276
+ {
277
+ "query": """
278
+ sequence by agent.id
279
+ [ file where file.name == "cmd.exe" ]
280
+ [ process where stringContains(process.name, "regsvr32") ]
281
+ """
282
+ }
283
+ ----
284
+
285
+ The API returns the following response. The `hits.sequences.join_keys` property
286
+ contains the shared `agent.id` value for each matching event.
287
+
288
+ [source,console-result]
289
+ ----
290
+ {
291
+ "took": 60,
292
+ "timed_out": false,
293
+ "hits": {
294
+ "total": {
295
+ "value": 1,
296
+ "relation": "eq"
297
+ },
298
+ "sequences": [
299
+ {
300
+ "join_keys": [
301
+ "8a4f500d"
302
+ ],
303
+ "events": [
304
+ {
305
+ "_index": "sec_logs",
306
+ "_id": "4",
307
+ "_score": null,
308
+ "_source": {
309
+ "@timestamp": "2020-12-07T11:07:08.000Z",
310
+ "agent": {
311
+ "id": "8a4f500d"
312
+ },
313
+ "event": {
314
+ "category": "file"
315
+ },
316
+ "file": {
317
+ "accessed": "2020-12-07T11:07:08.000Z",
318
+ "name": "cmd.exe",
319
+ "path": "C:\\Windows\\System32\\cmd.exe",
320
+ "type": "file",
321
+ "size": 16384
322
+ },
323
+ "process": {
324
+ "name": "cmd.exe",
325
+ "path": "C:\\Windows\\System32\\cmd.exe"
326
+ }
327
+ },
328
+ "fields": {
329
+ "@timestamp": [
330
+ "1607339228000"
331
+ ]
332
+ },
333
+ "sort": [
334
+ 1607339228000
335
+ ]
336
+ },
337
+ {
338
+ "_index": "sec_logs",
339
+ "_id": "5",
340
+ "_score": null,
341
+ "_source": {
342
+ "@timestamp": "2020-12-07T11:07:09.000Z",
343
+ "agent": {
344
+ "id": "8a4f500d"
345
+ },
346
+ "event": {
347
+ "category": "process"
348
+ },
349
+ "process": {
350
+ "name": "regsvr32.exe",
351
+ "path": "C:\\Windows\\System32\\regsvr32.exe"
352
+ }
353
+ },
354
+ "fields": {
355
+ "@timestamp": [
356
+ "1607339229000"
357
+ ]
358
+ },
359
+ "sort": [
360
+ 1607339229000
361
+ ]
362
+ }
363
+ ]
364
+ }
365
+ ]
366
+ }
367
+ }
368
+ ----
369
+ // TESTRESPONSE[s/"took": 60/"took": $body.took/]
370
+ ====
371
+
130
372
[discrete]
131
373
[[eql-search-specify-event-category-field]]
132
374
=== Specify an event category field
@@ -145,7 +387,7 @@ field.
145
387
----
146
388
GET /sec_logs/_eql/search
147
389
{
148
- "event_category_field": "file.type",
390
+ "event_category_field": "file.type",
149
391
"query": """
150
392
file where agent.id == "8a4f500d"
151
393
"""
0 commit comments