eql.asciidoc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. [role="xpack"]
  2. [testenv="basic"]
  3. [[eql]]
  4. = EQL search
  5. ++++
  6. <titleabbrev>EQL</titleabbrev>
  7. ++++
  8. beta::[]
  9. Event Query Language (EQL) is a query language for event-based time series
  10. data, such as logs, metrics, and traces.
  11. [discrete]
  12. [[eql-advantages]]
  13. == Advantages of EQL
  14. * *EQL lets you express relationships between events.* +
  15. Many query languages allow you to match single events. EQL lets you match a
  16. sequence of events across different event categories and time spans.
  17. * *EQL has a low learning curve.* +
  18. <<eql-syntax,EQL syntax>> looks like other common query languages, such as SQL.
  19. EQL lets you write and read queries intuitively, which makes for quick,
  20. iterative searching.
  21. * *EQL is designed for security use cases.* +
  22. While you can use it for any event-based data, we created EQL for threat
  23. hunting. EQL not only supports indicator of compromise (IOC) searches but can
  24. describe activity that goes beyond IOCs.
  25. [discrete]
  26. [[eql-required-fields]]
  27. == Required fields
  28. To run an EQL search, the searched data stream or index must contain a
  29. _timestamp_ and _event category_ field. By default, EQL uses the `@timestamp`
  30. and `event.category` fields from the {ecs-ref}[Elastic Common Schema
  31. (ECS)]. To use a different timestamp or event category field, see
  32. <<specify-a-timestamp-or-event-category-field>>.
  33. TIP: While no schema is required to use EQL, we recommend using the
  34. {ecs-ref}[ECS]. EQL searches are designed to work with core ECS fields by
  35. default.
  36. [discrete]
  37. [[run-an-eql-search]]
  38. == Run an EQL search
  39. Use the <<eql-search-api,EQL search API>> to run a <<eql-basic-syntax,basic
  40. EQL query>>:
  41. [source,console]
  42. ----
  43. GET /my-index-000001/_eql/search
  44. {
  45. "query": """
  46. process where process.name == "regsvr32.exe"
  47. """
  48. }
  49. ----
  50. // TEST[setup:sec_logs]
  51. By default, basic EQL queries return the top 10 matching events in the
  52. `hits.events` property. These hits are sorted by timestamp, converted to
  53. milliseconds since the {wikipedia}/Unix_time[Unix epoch], in ascending order.
  54. [source,console-result]
  55. ----
  56. {
  57. "is_partial": false,
  58. "is_running": false,
  59. "took": 60,
  60. "timed_out": false,
  61. "hits": {
  62. "total": {
  63. "value": 2,
  64. "relation": "eq"
  65. },
  66. "events": [
  67. {
  68. "_index": "my-index-000001",
  69. "_id": "OQmfCaduce8zoHT93o4H",
  70. "_source": {
  71. "@timestamp": "2099-12-07T11:07:09.000Z",
  72. "event": {
  73. "category": "process",
  74. "id": "aR3NWVOs",
  75. "sequence": 4
  76. },
  77. "process": {
  78. "pid": 2012,
  79. "name": "regsvr32.exe",
  80. "command_line": "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll",
  81. "executable": "C:\\Windows\\System32\\regsvr32.exe"
  82. }
  83. }
  84. },
  85. {
  86. "_index": "my-index-000001",
  87. "_id": "xLkCaj4EujzdNSxfYLbO",
  88. "_source": {
  89. "@timestamp": "2099-12-07T11:07:10.000Z",
  90. "event": {
  91. "category": "process",
  92. "id": "GTSmSqgz0U",
  93. "sequence": 6,
  94. "type": "termination"
  95. },
  96. "process": {
  97. "pid": 2012,
  98. "name": "regsvr32.exe",
  99. "executable": "C:\\Windows\\System32\\regsvr32.exe"
  100. }
  101. }
  102. }
  103. ]
  104. }
  105. }
  106. ----
  107. // TESTRESPONSE[s/"took": 60/"took": $body.took/]
  108. // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.events.0._id/]
  109. // TESTRESPONSE[s/"_id": "xLkCaj4EujzdNSxfYLbO"/"_id": $body.hits.events.1._id/]
  110. Use the `size` parameter to get a smaller or larger set of hits:
  111. [source,console]
  112. ----
  113. GET /my-index-000001/_eql/search
  114. {
  115. "query": """
  116. process where process.name == "regsvr32.exe"
  117. """,
  118. "size": 50
  119. }
  120. ----
  121. // TEST[setup:sec_logs]
  122. [discrete]
  123. [[eql-search-sequence]]
  124. === Search for a sequence of events
  125. Use EQL's <<eql-sequences,sequence syntax>> to search for a series of
  126. ordered events. List the event items in ascending chronological order,
  127. with the most recent event listed last:
  128. [source,console]
  129. ----
  130. GET /my-index-000001/_eql/search
  131. {
  132. "query": """
  133. sequence
  134. [ process where process.name == "regsvr32.exe" ]
  135. [ file where stringContains(file.name, "scrobj.dll") ]
  136. """
  137. }
  138. ----
  139. // TEST[setup:sec_logs]
  140. Matching sequences are returned in the `hits.sequences` property.
  141. [source,console-result]
  142. ----
  143. {
  144. "is_partial": false,
  145. "is_running": false,
  146. "took": 60,
  147. "timed_out": false,
  148. "hits": {
  149. "total": {
  150. "value": 1,
  151. "relation": "eq"
  152. },
  153. "sequences": [
  154. {
  155. "events": [
  156. {
  157. "_index": "my-index-000001",
  158. "_id": "OQmfCaduce8zoHT93o4H",
  159. "_source": {
  160. "@timestamp": "2099-12-07T11:07:09.000Z",
  161. "event": {
  162. "category": "process",
  163. "id": "aR3NWVOs",
  164. "sequence": 4
  165. },
  166. "process": {
  167. "pid": 2012,
  168. "name": "regsvr32.exe",
  169. "command_line": "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll",
  170. "executable": "C:\\Windows\\System32\\regsvr32.exe"
  171. }
  172. }
  173. },
  174. {
  175. "_index": "my-index-000001",
  176. "_id": "yDwnGIJouOYGBzP0ZE9n",
  177. "_source": {
  178. "@timestamp": "2099-12-07T11:07:10.000Z",
  179. "event": {
  180. "category": "file",
  181. "id": "tZ1NWVOs",
  182. "sequence": 5
  183. },
  184. "process": {
  185. "pid": 2012,
  186. "name": "regsvr32.exe",
  187. "executable": "C:\\Windows\\System32\\regsvr32.exe"
  188. },
  189. "file": {
  190. "path": "C:\\Windows\\System32\\scrobj.dll",
  191. "name": "scrobj.dll"
  192. }
  193. }
  194. }
  195. ]
  196. }
  197. ]
  198. }
  199. }
  200. ----
  201. // TESTRESPONSE[s/"took": 60/"took": $body.took/]
  202. // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.sequences.0.events.0._id/]
  203. // TESTRESPONSE[s/"_id": "yDwnGIJouOYGBzP0ZE9n"/"_id": $body.hits.sequences.0.events.1._id/]
  204. Use the <<eql-with-maxspan-keywords,`with maxspan` keywords>> to constrain
  205. matching sequences to a timespan:
  206. [source,console]
  207. ----
  208. GET /my-index-000001/_eql/search
  209. {
  210. "query": """
  211. sequence with maxspan=1h
  212. [ process where process.name == "regsvr32.exe" ]
  213. [ file where stringContains(file.name, "scrobj.dll") ]
  214. """
  215. }
  216. ----
  217. // TEST[setup:sec_logs]
  218. Use the <<eql-by-keyword,`by` keyword>> to match events that share the
  219. same field values:
  220. [source,console]
  221. ----
  222. GET /my-index-000001/_eql/search
  223. {
  224. "query": """
  225. sequence with maxspan=1h
  226. [ process where process.name == "regsvr32.exe" ] by process.pid
  227. [ file where stringContains(file.name, "scrobj.dll") ] by process.pid
  228. """
  229. }
  230. ----
  231. // TEST[setup:sec_logs]
  232. If a field value should be shared across all events, use the `sequence by`
  233. keyword. The following query is equivalent to the previous one.
  234. [source,console]
  235. ----
  236. GET /my-index-000001/_eql/search
  237. {
  238. "query": """
  239. sequence by process.pid with maxspan=1h
  240. [ process where process.name == "regsvr32.exe" ]
  241. [ file where stringContains(file.name, "scrobj.dll") ]
  242. """
  243. }
  244. ----
  245. // TEST[setup:sec_logs]
  246. The `hits.sequences.join_keys` property contains the shared field values.
  247. [source,console-result]
  248. ----
  249. {
  250. "is_partial": false,
  251. "is_running": false,
  252. "took": 60,
  253. "timed_out": false,
  254. "hits": {
  255. "total": {
  256. "value": 1,
  257. "relation": "eq"
  258. },
  259. "sequences": [
  260. {
  261. "join_keys": [
  262. 2012
  263. ],
  264. "events": [
  265. {
  266. "_index": "my-index-000001",
  267. "_id": "OQmfCaduce8zoHT93o4H",
  268. "_source": {
  269. "@timestamp": "2099-12-07T11:07:09.000Z",
  270. "event": {
  271. "category": "process",
  272. "id": "aR3NWVOs",
  273. "sequence": 4
  274. },
  275. "process": {
  276. "pid": 2012,
  277. "name": "regsvr32.exe",
  278. "command_line": "regsvr32.exe /s /u /i:https://...RegSvr32.sct scrobj.dll",
  279. "executable": "C:\\Windows\\System32\\regsvr32.exe"
  280. }
  281. }
  282. },
  283. {
  284. "_index": "my-index-000001",
  285. "_id": "yDwnGIJouOYGBzP0ZE9n",
  286. "_source": {
  287. "@timestamp": "2099-12-07T11:07:10.000Z",
  288. "event": {
  289. "category": "file",
  290. "id": "tZ1NWVOs",
  291. "sequence": 5
  292. },
  293. "process": {
  294. "pid": 2012,
  295. "name": "regsvr32.exe",
  296. "executable": "C:\\Windows\\System32\\regsvr32.exe"
  297. },
  298. "file": {
  299. "path": "C:\\Windows\\System32\\scrobj.dll",
  300. "name": "scrobj.dll"
  301. }
  302. }
  303. }
  304. ]
  305. }
  306. ]
  307. }
  308. }
  309. ----
  310. // TESTRESPONSE[s/"took": 60/"took": $body.took/]
  311. // TESTRESPONSE[s/"_id": "OQmfCaduce8zoHT93o4H"/"_id": $body.hits.sequences.0.events.0._id/]
  312. // TESTRESPONSE[s/"_id": "yDwnGIJouOYGBzP0ZE9n"/"_id": $body.hits.sequences.0.events.1._id/]
  313. Use the <<eql-until-keyword,`until` keyword>> to specify an expiration
  314. event for sequences. Matching sequences must end before this event.
  315. [source,console]
  316. ----
  317. GET /my-index-000001/_eql/search
  318. {
  319. "query": """
  320. sequence by process.pid with maxspan=1h
  321. [ process where process.name == "regsvr32.exe" ]
  322. [ file where stringContains(file.name, "scrobj.dll") ]
  323. until [ process where event.type == "termination" ]
  324. """
  325. }
  326. ----
  327. // TEST[setup:sec_logs]
  328. [discrete]
  329. [[specify-a-timestamp-or-event-category-field]]
  330. === Specify a timestamp or event category field
  331. The EQL search API uses the `@timestamp` and `event.category` fields from the
  332. {ecs-ref}[ECS] by default. To specify different fields, use the
  333. `timestamp_field` and `event_category_field` parameters:
  334. [source,console]
  335. ----
  336. GET /my-index-000001/_eql/search
  337. {
  338. "timestamp_field": "file.accessed",
  339. "event_category_field": "file.type",
  340. "query": """
  341. file where (file.size > 1 and file.type == "file")
  342. """
  343. }
  344. ----
  345. // TEST[setup:sec_logs]
  346. The event category field must be mapped as a <<keyword,`keyword`>> family field
  347. type. The timestamp field should be mapped as a <<date,`date`>> field type.
  348. <<date_nanos,`date_nanos`>> timestamp fields are not supported. You cannot use a
  349. <<nested,`nested`>> field or the sub-fields of a `nested` field as the timestamp
  350. or event category field.
  351. [discrete]
  352. [[eql-search-specify-a-sort-tiebreaker]]
  353. === Specify a sort tiebreaker
  354. By default, the EQL search API returns matching hits by timestamp. If two or
  355. more events share the same timestamp, {es} uses a tiebreaker field value to sort
  356. the events in ascending, lexicographic order.
  357. If you don't specify a tiebreaker field or the events also share the same
  358. tiebreaker value, {es} considers the events concurrent. Concurrent events cannot
  359. be part of the same sequence and may not be returned in a consistent sort order.
  360. To specify a tiebreaker field, use the `tiebreaker_field` parameter. If you
  361. specify a tiebreaker field for a sequence query, all events in the searched data
  362. streams or indices must contain a tiebreaker field value. For basic queries,
  363. {es} orders matching events with no tiebreaker value after events with a
  364. tiebreaker value.
  365. If you use the {ecs-ref}[ECS], we recommend using `event.sequence` as the
  366. tiebreaker field.
  367. [source,console]
  368. ----
  369. GET /my-index-000001/_eql/search
  370. {
  371. "tiebreaker_field": "event.sequence",
  372. "query": """
  373. process where process.name == "cmd.exe" and stringContains(process.executable, "System32")
  374. """
  375. }
  376. ----
  377. // TEST[setup:sec_logs]
  378. [discrete]
  379. [[eql-search-filter-query-dsl]]
  380. === Filter using query DSL
  381. The `filter` parameter uses <<query-dsl,query DSL>> to limit the documents on
  382. which an EQL query runs.
  383. [source,console]
  384. ----
  385. GET /my-index-000001/_eql/search
  386. {
  387. "filter": {
  388. "range" : {
  389. "file.size" : {
  390. "gte" : 1,
  391. "lte" : 1000000
  392. }
  393. }
  394. },
  395. "query": """
  396. file where (file.type == "file" and file.name == "cmd.exe")
  397. """
  398. }
  399. ----
  400. // TEST[setup:sec_logs]
  401. [discrete]
  402. [[eql-search-async]]
  403. === Run an async EQL search
  404. By default, EQL search requests are synchronous and wait for complete results
  405. before returning a response. However, complete results can take longer for
  406. searches across <<frozen-indices,frozen indices>> or
  407. <<modules-cross-cluster-search,multiple clusters>>.
  408. To avoid long waits, run an async EQL search. Set the
  409. `wait_for_completion_timeout` parameter to a duration you'd like to wait for
  410. synchronous results.
  411. [source,console]
  412. ----
  413. GET /frozen-my-index-000001/_eql/search
  414. {
  415. "wait_for_completion_timeout": "2s",
  416. "query": """
  417. process where process.name == "cmd.exe"
  418. """
  419. }
  420. ----
  421. // TEST[setup:sec_logs]
  422. // TEST[s/frozen-my-index-000001/my-index-000001/]
  423. If the request doesn't finish within the timeout period, the search becomes async
  424. and returns a response that includes:
  425. * A search ID
  426. * An `is_partial` value of `true`, indicating the search results are
  427. incomplete
  428. * An `is_running` value of `true`, indicating the search is ongoing
  429. The async search continues to run in the background without blocking other
  430. requests.
  431. [source,console-result]
  432. ----
  433. {
  434. "id": "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
  435. "is_partial": true,
  436. "is_running": true,
  437. "took": 2000,
  438. "timed_out": false,
  439. "hits": ...
  440. }
  441. ----
  442. // TESTRESPONSE[s/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=/$body.id/]
  443. // TESTRESPONSE[s/"is_partial": true/"is_partial": $body.is_partial/]
  444. // TESTRESPONSE[s/"is_running": true/"is_running": $body.is_running/]
  445. // TESTRESPONSE[s/"took": 2000/"took": $body.took/]
  446. // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
  447. To check the progress of an async search, use the <<get-async-eql-search-api,get
  448. async EQL search API>> with the search ID. Specify how long you'd like for
  449. complete results in the `wait_for_completion_timeout` parameter.
  450. [source,console]
  451. ----
  452. GET /_eql/search/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=?wait_for_completion_timeout=2s
  453. ----
  454. // TEST[skip: no access to search ID]
  455. If the response's `is_running` value is `false`, the async search has finished.
  456. If the `is_partial` value is `false`, the returned search results are
  457. complete.
  458. [source,console-result]
  459. ----
  460. {
  461. "id": "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
  462. "is_partial": false,
  463. "is_running": false,
  464. "took": 2000,
  465. "timed_out": false,
  466. "hits": ...
  467. }
  468. ----
  469. // TESTRESPONSE[s/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=/$body.id/]
  470. // TESTRESPONSE[s/"took": 2000/"took": $body.took/]
  471. // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
  472. [discrete]
  473. [[eql-search-store-async-eql-search]]
  474. === Change the search retention period
  475. By default, the EQL search API stores async searches for five days. After this
  476. period, any searches and their results are deleted. Use the `keep_alive`
  477. parameter to change this retention period:
  478. [source,console]
  479. ----
  480. GET /my-index-000001/_eql/search
  481. {
  482. "keep_alive": "2d",
  483. "wait_for_completion_timeout": "2s",
  484. "query": """
  485. process where process.name == "cmd.exe"
  486. """
  487. }
  488. ----
  489. // TEST[setup:sec_logs]
  490. You can use the <<get-async-eql-search-api,get async EQL search API>>'s
  491. `keep_alive` parameter to later change the retention period. The new retention
  492. period starts after the get request runs.
  493. [source,console]
  494. ----
  495. GET /_eql/search/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=?keep_alive=5d
  496. ----
  497. // TEST[skip: no access to search ID]
  498. Use the <<delete-async-eql-search-api,delete async EQL search API>> to
  499. manually delete an async EQL search before the `keep_alive` period ends. If the
  500. search is still ongoing, {es} cancels the search request.
  501. [source,console]
  502. ----
  503. DELETE /_eql/search/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=?keep_alive=5d
  504. ----
  505. // TEST[skip: no access to search ID]
  506. [discrete]
  507. [[eql-search-store-sync-eql-search]]
  508. === Store synchronous EQL searches
  509. By default, the EQL search API only stores async searches. To save a synchronous
  510. search, set `keep_on_completion` to `true`:
  511. [source,console]
  512. ----
  513. GET /my-index-000001/_eql/search
  514. {
  515. "keep_on_completion": true,
  516. "wait_for_completion_timeout": "2s",
  517. "query": """
  518. process where process.name == "cmd.exe"
  519. """
  520. }
  521. ----
  522. // TEST[setup:sec_logs]
  523. The response includes a search ID. `is_partial` and `is_running` are `false`,
  524. indicating the EQL search was synchronous and returned complete results.
  525. [source,console-result]
  526. ----
  527. {
  528. "id": "FjlmbndxNmJjU0RPdExBTGg0elNOOEEaQk9xSjJBQzBRMldZa1VVQ2pPa01YUToxMDY=",
  529. "is_partial": false,
  530. "is_running": false,
  531. "took": 52,
  532. "timed_out": false,
  533. "hits": ...
  534. }
  535. ----
  536. // TESTRESPONSE[s/FjlmbndxNmJjU0RPdExBTGg0elNOOEEaQk9xSjJBQzBRMldZa1VVQ2pPa01YUToxMDY=/$body.id/]
  537. // TESTRESPONSE[s/"took": 52/"took": $body.took/]
  538. // TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
  539. Use the <<get-async-eql-search-api,get async EQL search API>> to get the
  540. same results later:
  541. [source,console]
  542. ----
  543. GET /_eql/search/FjlmbndxNmJjU0RPdExBTGg0elNOOEEaQk9xSjJBQzBRMldZa1VVQ2pPa01YUToxMDY=
  544. ----
  545. // TEST[skip: no access to search ID]
  546. Saved synchronous searches are still subject to the `keep_alive` parameter's
  547. retention period. When this period ends, the search and its results are deleted.
  548. You can also manually delete saved synchronous searches using the
  549. <<delete-async-eql-search-api,delete async EQL search API>>.
  550. include::syntax.asciidoc[]
  551. include::functions.asciidoc[]
  552. include::pipes.asciidoc[]
  553. include::detect-threats-with-eql.asciidoc[]